WPF How to refresh a user control - c#

I tried UIElement.InvalidateVisual but it didn't work. I read about this INotifyPropertyChanged but I don't know exactly how it is used, not to mention I'm talking about User Controls, it seems I need to use that INotifyPropertyChanged to all of the controls.
I'm having trouble refreshing combo boxes changes and data grid changes, what I did is to close and reopen the form but I don't want that approach because it seems sluggish in re-executing every user control constructors.
this is my code:
if (linkSource.ToString() == "BreedList.xaml")
{
this.InvalidateVisual();
}
else if (linkSource.ToString() == "PetTypeList.xaml")
{
this.InvalidateVisual();
}
else if (linkSource.ToString() == "IrritantList.xaml")
{
this.InvalidateVisual();
}

This answer is an attempt to give you a peek into how WPF applications work, but I am not offering to teach you WPF... that is your job.
As #HighCore correctly pointed out, there is no UserControl.Refresh method. In a WPF Application (using MVVM), we create custom class objects called view models that contain the data and functionality that the UserControls and/or Windows (called views) need to provide. They are paired by setting the view DataContext property to an instance of the relevant view model. There are several ways to do that... this is just one way:
public SomeView()
{
...
DataContext = new SomeViewModel();
}
Now, all of the controls declared in SomeView have access to all of the (public) properties and ICommands from the SomeViewModel class. We use them with data binding:
<TextBox Text="{Binding SomePropertyInSomeViewModel}" ... />
...
<ListBox ItemsSource="{Binding SomeCollectionPropertyInSomeViewModel}" />
During initialisation of the SomeViewModel class, we might see something like this:
public SomeViewModel()
{
SomeCollectionPropertyInSomeViewModel = SomeDataSource.GetSomeCollection();
}
This gets the data from any external data source and populates the SomeCollectionPropertyInSomeViewModel property. In turn, the SomeCollectionPropertyInSomeViewModel property provides the collection items for the ListBox. So finally, to answer your question, How do we refresh the data?, the answer is to call the methods that get the data again. So you could do something like this:
public void RefreshData()
{
SomeCollectionPropertyInSomeViewModel = SomeDataSource.GetSomeCollection();
}
Ok, so here ends today's lesson. For any further questions, please refer to the proper Microsoft documentation on MSDN. For follow up information, please see the Introduction to WPF page on MSDN.

To add to previous answer: there are two ways to create "SomeViewModel". In both ways it should notify about a property change.
1) Use a DependencyObject. This is a standard WPF way, all UserControls are also DependencyObjects. Taken from here, read full explanation: http://wpftutorial.net/DependencyProperties.html
public class SomeViewModel : DependencyObject
{
// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty =
DependencyProperty.Register("CurrentTime", typeof(DateTime),
typeof(SomeViewModel), new FrameworkPropertyMetadata(DateTime.Now));
// .NET Property wrapper
public DateTime CurrentTime
{
get { return (DateTime)GetValue(CurrentTimeProperty); }
set { SetValue(CurrentTimeProperty, value); }
}
}
When CurrentTime changes everybody which is registered ( for example using {Binding CurrentTime} in XAML ) will get a notification and "Refresh"
2) Use INotifyPropertyChanged interface. Here's a small example:
http://msdn.microsoft.com/en-us/library/vstudio/ms229614(v=vs.100).aspx
There are some differences between this approaches ( for example you can't create binding to INotifyPropertyChanged property ). Please read here:
http://www.codeproject.com/Articles/62158/DependencyProperties-or-INotifyPropertyChanged
INotifyPropertyChanged vs. DependencyProperty in ViewModel
Good luck!

Related

How to (correctly) update the M in MVVM of WPF application?

Having passed a series of Edward Tanguay's questions refractoring the usage of MVVM for WPF app which can be found in Linked sidebar of his Fat Models, skinny ViewModels and dumb Views, the best MVVM approach?, I am a little confused by his
final WPF application in Big smart ViewModels, dumb Views, and any model, the best MVVM approach?
Its M (Model) is Customer class:
//model
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime TimeOfMostRecentActivity { get; set; }
public static Customer GetCurrentCustomer()
{
return new Customer
{ FirstName = "Jim"
, LastName = "Smith"
, TimeOfMostRecentActivity = DateTime.Now
};
}
}
which returns current user. Kind of, beause it returns duplicates of newly created "current" user...
But where is the M's data stored and updated in case of need?
Suppose, I want to change the model's current user's FirstName to "Gennady"?
I added a button for updating the model with this button click event handler:
private void button1_Click(object sender, RoutedEventArgs e)
{
}
aiming to change the model's data from it which will be reflected in GUI.
How can I do this, by clicking this button... sorry, by placing the code into this button1_Click()?
Or it is something wrong with my wish to do it?
Then. how to correctly update/change M in MVVM ?
Update:
All answers seem refer that I should not make changes in M but on VM.
Though I've specifically asked about referenced M-V-VM implementation with:
public CustomerViewModel()
{
_timer = new Timer(CheckForChangesInModel, null, 0, 1000);
}
private void CheckForChangesInModel(object state)
{
Customer currentCustomer = CustomerViewModel.GetCurrentCustomer();
MapFieldsFromModeltoViewModel(currentCustomer, this);
}
public static void MapFieldsFromModeltoViewModel
(Customer model, CustomerViewModel viewModel)
{
viewModel.FirstName = model.FirstName;
viewModel.LastName = model.LastName;
viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity;
}
So, for example, upon implementing the code from Adolfo Perez's answer changes, the TextBox's content is changed from "Jim" to "Gennady" only for a period of interval set in _timer = new Timer(CheckForChangesInModel, null, 0, 1000);.
All logic of referenced by me M-V-VM in WPF approach is such that it is "M" should be updated, in order VM has caught up those changes, but not the "VM".
Even more, I cannot understand, if to make changes in VM how can they be reflected in M if the VM knows about M but - not vice versa - Model does not know about ViewModel).
In MVVM you should avoid code-behind. The reason is that you want to end up with testable classes, in this case your VM's that are completely independent from your V. You could run a set of unit tests on your VM without involving the V. You could also hook different types of Views without affecting your business logic.
Your button will bind its Command property to an ICommand property exposed in your VM. This Command in your VM will handle your click event in the method you specify.
In your View:
<Button Content="Change FirstName"
Command="{Binding Path=ChangeFirstNameCommand"}/>
In your ViewModel:
//Define your command
public ICommand ChangeFirstNameCommand {get;set;}
//Initialize your command in Constructor perhaps
ChangeFirstNameCommand = new RelayCommand(OnChangeFirstName,CanChangeFirstName);
private void OnChangeFirstName()
{
//Your FirstName TextBox in your V will be updated after you click the Button
this.FirstName = "Gennady";
}
private bool CanChangeFirstName()
{
//Add any validation to set whether your button is enabled or not.
// WPF internals take care of this.
return true;
}
It is very important to keep in mind that in this pattern your V knows about your VM and your VM knows about your M but not the other way around.
In your example if you want to change your Model FirstName property you woud have to do the following:
Create a VM which implements INotifyPropertyChanged
Expose your M FirstName property in your VM notifying changes
Create a TextBox in your XAML View and bind its Text property to your VM.FirstName setting Binding Mode=TwoWay.
<TextBox Text=
"{Binding Path=FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
As you type in the TextBox your FirstName will be directly populated in the VM-M. Also, thanks to the Two way binding, if you modify your FirstName property in your VM, that change will be reflected automatically in your V
Set your View.DataContext to your VM. This is what sets the Context for all your data bindings, unless you specify a different binding source.
If you want to persist changes in a DB then inject a service class in your VM which will take care of CRUD operations
Take a look at this simple example:
http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute
Your model is your domain (business) objects. There are number of ways you can get them. For example you may have a repository class that gives you your data when you request it and handles the persistance when you wish to store it.
Your view-model is a class that handles UI logic, like updating fields, reacting on user actions, etc. In your case, you may pass an instance of CustomerRepository class to your view model. Then in view-model code you get the instance of Customer from the repository and fill your view-model properties on wich your UI elements are bound.
Your view is just a set of rules of how you wish to show the information to a user. It must be as declarative and logic free as possible.
Having a code like this:
private void button1_Click(object sender, RoutedEventArgs e)
{
}
in your view (or even worse - in your view-model) is a huge mistake wich breaks the pattern and may (and surely will) lead to troubles. You should bind ICommand fields of ViewModel to the buttons. You should not try to write a WPF application in a WinForm event-driven style.
That's how mvvm works in general and it's main purpose is to support multi-tier architecture in your application.
First off, you need to work on your V and VM.
If you are using a Click event for a button, you definatly aren't following this architecture.
You need to use WPF and XAML in your view to bind to your ViewModel, your ViewModel should be a subset of a particular or potentially many models and present the properties to the View which allows for binding.
I would also consider researching:
RelayCommand and ICommand for binding your buttons.
Repository pattern for interchanging your models and creating a way of CRUD
The tutorial which you have followed doesn't seem to be very good in that the concepts haven't really been put across properly or you haven't understood them.
If you have a pure WPF application it could be interesting to investigate the MVVM 'reverse' pattern, ViewModel-First.
For webdevelopment it is common to use MVVM because webpages get loaded through a browser and the View is constructed wich creates the ViewModel.
WPF users do not browse to pages (unless you use Page navigation) so it gets more interesting to follow VM-V-VM-M :
interface IMyView
Show();
//view implementations in different assemblies:
//silverlight
class SilverlightMyView:IMyView
Show();
//wpf
class WpfMyView:IMyView
Show();
class MyViewModel
IMyView _view;
MyModel _model;
//ex. view resolved by DI (Unity, ..)
MyViewModel(IMyView view)
_view = view
Show(model as MyModel)
_model = model;
_view.DataContext = this;
_view.Show();

Creating a helper class for ballontip that can be accessed from any wpf page

I have a wpf application that is using a NavigationWindow that allows me to load pages within the MasterWindow.
I am trying to add the notifyicon feature http://www.codeproject.com/KB/WPF/wpf_notifyicon.aspx to my application that will allow me to display a notification window when I want.
I have added the taskbaritem to my navigationwindow
<tb:TaskbarIcon x:Name="MyNotifyIcon" ToolTipText="My Application" IconSource="/Images/Icons/TB.ico"/>
I want to be able to create a helper class that can access MyNotifyIcon from any of the pages loaded. I was thinking something like
public static void DisplayMessageArea(string messageToDisplay)
{
var balloon = new StandardNotification {BalloonMessage = messageToDisplay };
//TaskbarIcon tb = (TaskbarIcon)MasterWindow.Resources.FindName("MyNotifyIcon");
//tb.ShowCustomBalloon(balloon,PopupAnimation.Slide,4000);
}
However I don't know the correct way to find the controller "MyNotifyIcon" from my helper class.
Can someone point me in the right direction?
I've never seen that NotifyIcon before (kinda cool, by the way), but I took a look at its API. What I would suggest is that you not bother naming that control and trying to find it in your code. Instead, in your main navigation window, set its datacontext to a helper class that you define, and bind to its iconsource, tooltiptext, etc:
<tb:TaskbarIcon DataContext="{Binding SomeViewModel}"
IconSource="..."
ToolTipText="{Binding Text}"
Visibility="{Binding IsVisible, Converter=...}" />
(More about value converters here).
Now, the "SomeViewModel" class will implement INotifyPropertyChanged and expose properties like:
bool IsVisible { get; set; }
string Text { get; set; }
...except that these properties will have actual getters/setters and raise the property changed event.
With this approach, you don't need to tell your presentation layer to go looking in your View layer, which isn't really ideal. It's better if your view (XAML) knows about your presentation (code), but not vice-versa, as this loosens coupling. In this case, it loosens coupling by virtue of the fact that your actual code does not depend on the existence of some named control declared in XAML.
As to how to get at this helper view model, you can pass a reference to it around to the various classes that can set it, or you can have the sub-controls of the navigation window raise events that the nav window listens for, or you can go with the approach that you had in mind, which is to define a static method on the view model (I'm not an advocate of this approach, personally, but it's the closest to what you're looking to do).
If you're set on the approach that you're taking here, however, keep in mind that you do have it named in that control, so you can re-expose it as a static property on the control in question:
class MyControlWithTipIcon
{
public static TaskbarIcon TaskBarIcon { get { return MyNotifyIcon; } }
}
Now, you can access it from your helper class:
public static void DisplayMessageArea(string messageToDisplay)
{
MyControlWithTipIcon.TaskBarIcon.ToolTipText = messageToDisplay;
MyControlWithTipIcon.TaskBarIcon.Visibility = ... //i.e. show the message
}
I would not personally advocate that approach at all, but it is probably the easiest way to do, specifically, what you're asking. If you go this route, however, remember to check the TaskBarIcon static property for null before doing anything with it, and keep in mind that the static property will work return a value whether or not your control containing it has been loaded or even instantiated.

Getting lookup view model to trigger Modal Dialog

What's the right way to get my viewmodel to trigger a custom lookup control to throw up a modal dialog that essentially represents that lookup viewmodel? The custom lookup control's data context is that of the parent record view model. The lookup control also has another DependencyProperty that has it bound to a lookupviewmodel property on the parent record view model and this represents a sub lookupviewmodel.
METHOD 1) I currrently use an event on the lookupviewmodel that the custom control knows to listen for.
METHOD 2) I tried throwing a validation exception within the setter of the property on the lookupviewmodel that the lookup control's text propery is bound too. Then I hooked the ErrorEvent in the custom lookup control. But it seems that if the user "corrects" the value from within the dialog while in this event, the original value sticks. And worse, even after I call Validation.ClearInvalid, another ErrorEvent still fires that somehow adds the error back. So everything works here in the sense that all the viewmodels have the correct data, it's just that it seems like the textbox is ignoring that the bound text property has changed on the underlying data source when inside an ErrorEvent. So it seems like I can't correct an error while inside the processing of that error?
Another sub issue within method 2 is that Validation.ClearInvalid doesn't remove the red error border. I had to manually clear the ErrorTemplate too. Is that right?
I'd like to find a way to use natural error handling within the control to get it to throw up the modal dialog.
This isn't what you use events for. Events exist to facilitate decoupling: the object raising the event shouldn't know or care what the object(s) listening to it are doing. You're expecting an event to be able to change the value of a property from inside the property's setter - or worse, your event handler is calling the very property setter that's raising the event that it's handling, which means that you have to do something pretty hackish to avoid a stack overflow.
Your description isn't very clear (you're describing both the problem you're having and the non-working solutions you're trying at the same time, which is confusing), but it sounds like what you're trying to do is something more like:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = GetValueFromDialog();
}
The problem is that you don't want to have code in your view model that throws up a dialog, since that creates a view model that can't be tested outside of your WPF application.
The answer in this case is to use dependency injection. Create an interface called IDialogService:
interface IDialogService
{
object GetValueFromDialog();
}
Now add this property to your view model:
public IDialogService DialogService { get; set; }
The above code becomes:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = DialogService.GetValueFromDialog();
}
Create a dialog service for use in your WPF application that actually throws up the dialog and gets the result. When you instantiate your view model in your application, do this:
MyViewModel vm = new MyViewModel { DialogService = new WpfDialogService(); }
Thus, in your application, the property setter will put up the dialog and get the result exactly as you expect it to.
For your unit tests, create a mock dialog that looks like this:
public class MockDialogService : IDialogService
{
private object Result;
public MockDialogService(object result)
{
Result = result;
}
public object GetValueFromDialog() { return Result; }
}
You can then write a test like:
MyViewModel vm = new MyViewModel { DialogService = MockDialogService(ExpectedResult) };
vm.Property = InvalidValue;
Assert.AreEqual(ExpectedResult, vm.Property);
The above is really more a sketch of a solution than a solution - depending on how your application uses dialogs, you may need a lot more features than what are sketched out here. If you take a look at MVVM frameworks you'll find that a lot of them implement dialog services of one kind or another.
You can use a framework like MVVMLight or Prism which allow you to pass payloads between different entities in totally decoupled ways. MVVMLight is very lightweight compared to Prism. It has a concept of Messanger which acts as a system wide event bus. Similarly you have EventAggregator in Prism.

Extending a solution for simple binding to a 'Text property to multiple Controls to handle binding to any Type?

My question is : how to move beyond writing a custom implementation of a technique for databinding multiple controls (controls without built-in DataSource properties), for each possible type of data, to simple properties ... as described and demonstrated in code that follows ... to achieve a more poweful solution that will be independent of whether the binding is to a string, or an int, or other types.
My guess is: this will involve reflection; but, I'm stuck at that point. I'm looking for strategic advice on which "direction" to move next, hints, clues, not a complete code answer, but of course I appreciate all responses, and I'll sure study code if you post code in reply ! Marc Clifton's 2005 article on CodeProject Simple Databinding: appears to demonstrate a reflection based approach: but, honestly, I do not really grok his code, and, in terms of .NET, 2005 is a long time ago.
Background: Partly in response to various SO questions and answers, like: Update Usercontrol on Three Forms: I've evolved a successful technique for databinding text properties of various controls simultaneously to one source defined in a Public class; also been able to "abstract" some of the details of the binding process using a static class that defines one extension method, and two public methods.
I've verifed that TextBoxes on Controls in a "MainForm," TextBoxes on a UserControl on the MainForm, and a TextBox on a second Form opened "independently" (i.e., form2.Parent == null) all update properly (i.e., two-way binding is in effect) from the "DataSource equivalent" public class. Change one: change all.
Code: an instance of this class will supply the target property (theText) for databinding:
public class TextDataBinder
{
public event PropertyChangedEventHandler PropertyChanged;
private string _theText;
public string theText
{
get { return _theText; }
// note : if 'setter is declared 'internal : blocks
// auto-updating when run-time user modifies consumers
// but will still allow update via code
set
{
_theText = value;
OnPropertyChanged(new PropertyChangedEventArgs("theText"));
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, e);
}
}
}
Code: this static class enables hiding some of the binding process complexity, and allows easy binding to multiple controls:
public static class TextBindingExtender
{
public static TextDataBinder CurrentDataSource;
public static void SetCurrentDataSource(TextDataBinder newCurrentDataSource)
{
CurrentDataSource = newCurrentDataSource;
}
// extension method for Control
public static void AddTextBinding(this Control theControl, string controlPropertyName, string targetPropertyName)
{
theControl.DataBindings.Add(controlPropertyName, CurrentDataSource, targetPropertyName, false, DataSourceUpdateMode.OnPropertyChanged);
}
// bind to all Controls in a List<Control>
public static void AddTextBindings(List<Control> theControls, string controlPropertyName, string targetPropertyName)
{
foreach (Control theControl in theControls)
{
theControl.AddTextBinding(controlPropertyName, targetPropertyName);
}
}
}
How the above classes are used (in a Form Load event) :
// create a new TextDataBinder
TextBindingExtender.CurrentDataSource = new TextDataBinder();
// bind to multiple textboxes, label, on a UserControl, on another Form, etc.
TextBindingExtender.AddTextBindings(new List<Control> { textBox1, textBox2, userControl11.tb, label1, instanceOfForm2.tb }, "Text", "theText");
// test assigning some initial text to the bound property
TextBindingExtender.CurrentDataSource.theText = "some initial text";
It really depends what you want to do; but ultimately common data-binding (for simple properties, done manually) consists of:
obtaining a property; preferably via TypeDescriptor.GetProperties(obj)[propName], giving you an abstraction (PropertyDescriptor)
asking the property if it is read-only (.IsReadOnly)
obtain (or set) the value (.GetValue(), .SetValue())
asking it for a converter to format / parse the value (.Converter, .ConvertFromString(), .ConvertToString()) THIS is a key bit that means you don't have to worry about what the data type is
asking it for the caption (.DisplayName, or .Name if that it empty/null)
asking it if it supports property-specific notification (.SupportsChangeEvents)
asking it to add/remove a change handler (.AddValueChanged(), .RemoveValueChanged())
you might also want to look at whether the object supports centralised notification (look for INotifyPropertyChanged)
If you might be binding to a list rather than a single object:
- the list might be abstracted behind IListSource
- the list might have custom properties, so check for ITypedList
- otherwise, identify the Type of the items and use TypeDescriptor.GetProperties(type)
- you need to consider a "currency manager" (i.e. should all the things bound to the same list be pointing to the same record in the list all the time)
There are also things like ICustomTypeDescriptor and TypeDescriptionProvider to consider, but most of the time TypeDescriptor handles this for you automatically.
As you can see - lots of things to think about! Lots of work... the one thing that you don't have to do is reflection; this is abstracted behind PropertyDescriptor. The reason for this is that not all data is static-typed; think about DataTable - the columns (which map to bindable data properties) are not fixed at compile-time, so reflection isn't appropriate. Likewise, some other types have custom "property bag" implementations. PropertyDescriptor lets your code handle either dynamic (not in the 4.0 sense) and reflective properties identically. It also works nicely with things like "HyperDescriptor", another property customisation.

How to bind wpf ComboBox to Linq to SQL Foreign Key Property

I have a Linq to SQL EntitySet with a Foreign Key Relationship between two tables. The tables in question are a Task table (called Issues) and a Departments Table. The foreign key is the department name (Which is guaranteed unique). I am running into an issue in that you cannot change a Linq to SQL FK field if the corresponding data is loaded.
The ViewModel (Not true MVVM, I am trying to practice the concepts while learning xaml, WPF and Linq-SQL)
public class StatusBoardViewModel : INotifyPropertyChanged
{
OIConsoleDataContext db = new OIConsoleDataContext();
private IQueryable<Issue> issues;
public IQueryable<Issue> Issues
{
get { // Lazy load issues if they have not been instantiated yet
if (issues == null) {
QueryIssues();
}
return issues;
}
set {
if (issues != value) {
issues = value;
OnPropertyChanged("Issues");
}
}
}
private IQueryable<Department> departments;
public IQueryable<Department> Departments
{
get {
// Lazy load departments if they have not been instantiated yet
if (departments == null) {
QueryDepartments();
}
return departments;
}
set {
if (departments != value) {
departments = value;
OnPropertyChanged("Departments");
}
}
}
private void QueryDepartments()
{
Departments = from d in db.Departments
orderby d.DeptName
select d;
}
public void QueryIssues()
{
Issues = from i in db.Issues
where i.IssIsOpen == true
orderby i.IssDueDate
select i;
// ....
}
#region INotifyPropertyChanged Members
}
My combo Box:
<ComboBox x:Name="DeptComboBox"
ItemsSource="{Binding Departments}"
DisplayMemberPath="DeptName"
SelectedValuePath="DeptName"
SelectedValue="{Binding Path=Issues/IssDepartment, Mode=TwoWay}"
Grid.Column="2" Grid.ColumnSpan="2" Grid.Row="3" Margin="6,7,115,5"
IsSynchronizedWithCurrentItem="True" />
As it stands it displays the combo box fine and defaults the selection to the correct department BUT if you make a change it throws an exception 'System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException'
I tried adding
SelectionChanged="DeptComboBox_SelectionChanged"
to the combo box then using the following code behind to remove the department reference then add the new one. It seemed to work fine but I discovered that if the filter settings caused the linq query to be changed then the current item before the change gets its department changed to the value of the new default items department when the Selection_Changed event fires. I could not find a way to tell if the selection changed event came from the user rather than the binding property to prevent this. Besides that I was not happy with the use of code where it seems it should be in a binding or at least some kind of value converter.
// This was an attempt to work around that did not work
// The referneces are passed from the Window1 event handler
public void ChangeDepartment(Issue currentIssue, Department currentDept)
{
if (currentIssue != null) {
currentIssue.Department = null;
currentIssue.Department = currentDept;
}
}
I have to be honest with myself, I am in over my pay grade here but that's when you learn the most eh..
How should I handle this? I have read a dozen articles on the issue and found them to be conflicting and as clear as mud.
Thanks for all your help SO
Mike
Update
I have had a very useful discussion with Chris Nicol below and he would probably already have the checkmark if I had a better handle on things. I think my biggest problem is that I am having to learn SQL, Linq, WPF and Database Design all at once. I have excellent books on all the subjects but they don't cover interoperation. For instance Pro Linq in C# 2008 is great... except it doesn't talk at all about Linq in WPF,
I have tried to do things as "MVVMish" as possible but, after a few false starts earlier, I decided it was one thing too many to learn at once. In practice I think I am close to MVVM except there is no commanding, I execute methods in the ViewModel class from the event handlers in the code behind. I am assuming that will make it fairly easy to refactor in the commanding structure later. My App is structured as follows:
The Model
Linq to SQL dbml files
partial classes for DataErrorInfo validation
The ViewModel
The DataContext
IQueryable collections for Tasks, Employees and Departments
A selected task property that is used by the add/edit windows view
Some properties for the view to bind to such as ShowDetailListItems() that triggers a different listbox ItemTemplate and is itself bound to a checkbox.
Methods to perform querys
Methods to open a window to add or edit a task and to submitChanges() upon return from said windows.
The View is bound to the viewmodel and the code behind contains:
a property that holds an instance of
the viewmodel class
code to instantiate the viewmodel in the contructor and to set the datacontext to the viewModel
The remainder of the code behind is event handlers that will be replaced by commands eventually
I have read Josh Smiths excellent article many times and explored the sample code. most of my format is based on that. The thread listed by Chris (Below) has a ton or new material for me to explore so I will spend some time digging into that and trying to determine how best to refactor things. This has gone far beyond some ComboBoxes not working, In fact part of the original issue was that the combo boxes were being bound to two different data contexts. Until I have a manageable architecture I think I am doing more harm that good trying to patch things up.
I will probably be back later with a checkmark for Chris
Grrr: "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext..."
I would recommend separating your data access layer from your ViewModel and using an observable collection. Once you leverage MVVM better this sort of thing isn't that hard.
I suggest that you read Josh Smith's article on MVVM, it will give you a great insight to WPF and it's binding capabilities. Accomplishing what you're trying to do in WPF is quite easy once you understand how it's working.
Good luck!

Categories

Resources