Update a DataGrid in MVVM - wpf

I have a DataGrid displaying rows from an Entity Framework Code First context. I am not sure about the best method to update automatically my DataGrid.
I am opening a new window where the user can add a new Costumer, once the user fills the form and clicks on the Save button, the window's ViewModel will add the Costumer to the Business Context, but the DataGrid displaying the Costumers is in the Main Window, controlled by the Main ViewModel.
I am not so sure about the best way to make the DataGrid show the recently added Costumer. I can only think about these ways:
I will create an ObservableCollection<Costumer> (as the DataGrid Data Binding Source) in the Main ViewModel and pass it to the Add New Costumer ViewModel's Constructor when the Main ViewModel creates this new window, so the new window's ViewModel can add the new Costumer in a new instance of the Business Context and add the Costumer to the ObservableCollection as well.
I can create a Business Context with an ObservableCollection<Costumer> inside it and update this collection every time it runs an Add Costumer method. The Business Context will have to be a Singleton throughout the entire app, being passed around of all the ViewModels and new windows the Main ViewModel creates.
I can make an Update Action on the Main ViewModel and invoke this Action from every New Window ViewModel. This Action will run a method to update the ObservableCollection<Costumer> in the Main ViewModel from the Business Context, thus updating the DataGrid.
I can make an Action or Func for every CRUD operation the app has to do on the Main ViewModel so every new ViewModel just invoke's it and the Main ViewModel will save it and update all references needed to the UI.
Are there any other better ways to accomplish this? I have seen some good examples of MVVM with Entity Framework Code First, but none of them have a logic that spans several ViewModels at the same time as mine does.
I am inclined to go with invoking Func in the Main ViewModel for every operation, I think this way is more clean of dependencies between ViewModels and all the data operations are limited to one class.

This should be pretty easy to achieve using the MVVM Light framework. MVVM Light uses a class called 'ViewModelLocator' to instantiate and track all of your ViewModels. For your scenario I'd have:
ViewModels:
MainViewModel
AddCostumerViewModel
Views:
MainView
AddCostumerView
The MainViewModel holds an ObservableCollection<Costumer>.
The AddCostumerViewModel holds a Costumer.
You bind controls on AddCostumerView to the properties of the Costumer in AddCostumerViewModel. You have a Button with a CommandBinding to a method in AddCostumerViewModel which includes something like:
_viewModelLocator.MainViewModel.Costumers.Add(newCostumer);
Your Costumer is added to the ObservableCollection<Costumer> in MainViewModel.
When the new Costumer is added to your ObservableCollection, NotifyPropertyChanged fires and your DataGrid updates.

Related

wpf mvvm binding List<int> to Listbox from background thread

I'm pretty new to wpf and mvvm so this may be a easy question but I'm hoping someone can explain it to me. I have a class in my model that all it does is polls processes and if it see that "calc" is running it adds it process id to a List. If you close calc.exe it removes it from the List. If the List changes it fires and event. In my ViewModel i subscribe to that event and update my property that fires off PropertyChanged. Now if my Property is a List in the view model then my binding in my view does not update correctly. If I changed my List to an ObservableCollection in my Model and ViewModel then i get a cross thread issue. However, if i leave my List in my model and change my property in my ViewModel to a ObservableCollection and copy the values of the the list into an observable collection everything works as expected.... I don't get it, do i really need to do the copy to get the binding in my ListBox to work properly?
Take a look at the DispatcherNotifiedObservableCollection that Cinch implements. It will automatically use the Dispatcher if need be. Read this article to see the code for it:
http://www.codeproject.com/KB/WPF/CinchIII.aspx#ObsCol
You need to update the observable collection from the dispatch thread.
Something like (not technically/syntactically correct, but psuedo-close)
Dispatcher.BeginInvoke( () => theList.Add( theThing ) );
A better way to achieve this functionality, is expose the Model via a property in the ViewModel, and bind the ListBox to Model.ObservableList. And use John's suggestion when adding and removing items to this list. This will not require you to copy the values from list to an observablecollection.
So your viewModel code would like
public class ViewModel
{
public Model Model { get { return _model; } }
}
The binding in xaml:
<ListBox ItemsSource=Model.ObservableList/>
where the ObservableList property returns your ObservableCollection.
As all UI elements in WPF have thread affinity (to the Dispatcher thread), any updates to the UI should be marshaled via the Dispatcher. You can marshal calls via the Dispatcher, using Dispatcher.Invoke. In this case, your view, viewModel and Model reside in the Dispatcher thread, so any call to update the UI via the viewModel or Model, would require you to call Dispatcher.Invoke.

Silverlight MVVM: MainView w/ SubViews OR MainViewModel w/SubViewModels

I have a MainView with a Tabbed UI.
How do I add a new TabItem (= View) to the TabControl?
Basicaly I see 2 ways:
1.)
* from code in the MainView I can add a new tab.
* the new tab contains a view with a referece to it's viewmodel.
2.)
* from code in the MainViewModel I can add a new viewmodel to a List of childViewModels
* the tabcontrol of the mainView is bound to that list
I prefere case #1 somehow, cause I think the view should know and instanciate it's VM (maybe by using the MVVM light ViewModelLocator) and not the other way round.
But how can I refere from the newly created VM to the MainVM? For example: the MainVM has a property 'IsAdmin'; how can I access (bind) that property from the SubViewModel?
alternative #2: how does the TabControl know, which view should be "rendered" for different ViewModels? How can I "map" from the SubViewModels to the corresponding "SubViews"?
Thanks for sharing your ideas!
I would check out this SO post as the answer can be applied to helping you with your problem.
In the spirit of MVVM, you will want to follow alternative #2. Let your ViewModel logic help you determine which "tabs" you need to display and use DataTemplates to represent those objects. Then you will get them bound to the DataContext of the View and your binding in the DataTemplate (View) will work correctly.
Thomas,
MVVM really is MVVMC. I would advise having a controller for the MainView which contains a method for creating a new tab. If the TabControl is complicated, you might put the functionality in the TabControl itself.
Separation of concerns (MODEL versus VIEWMODEL versus VIEW versus CONTROLLER) is compromised when actuation functionality is located in the models. (M or VM).
Regards,
Guido

MVVM scenario with main window and many user controls

I have a main window and many user controls that I want to show/hide depending on the user choice. For example there is a user control called Customer that should get all the customers from a database or a user control that is a form to sign up for a service. And so on. Each of this controls have viewmodel that should get the data from the database. The problem is I don't know hot to set the data context in the right way. My model is an ado.net entity data model for simplicity purposes.
I tried:
<UserControl.DataContext>
<vm:CustomerViewModel/>
</UserControl.DataContext>
And I'm binding the fields to the model fields but no data are visible. Before I used the methods in MainWindowViewModel and set the viewmodel to a view in App.xaml.cs and called the method:
Views.MainWindow newMainV = new Views.MainWindow();
ViewModels.MainVM mainVM = new ViewModels.MainVM();
mainVM.LoadCustomers();
newMainV.DataContext = mainVM;
newMainV.Show();
But I thought that the main window shoudl have its own viewmodel that would handle commands only and each user control should have its own viewmodel that will get the needed data.
What is wrong with my approach?
Thanks for any help.
In principle there's nothing wrong with setting the DataContext on a UserControl in the manner you've described. So I'd suspect there's something wrong in how your binding is set up, or in how your view model's constructor is working. You should be looking in the Output Window for binding errors and putting a breakpoint in the constructor to see what's actually happening when your UserControl gets instantiated.
You might have a look at the BookLibrary sample application of the WPF Application Framework (WAF).
This MVVM sample application uses Entity Framework data models as well and it is composed of various Views (UserControls).

Proper way in MVVM to direct the handling of a RelayCommand to a view model parent

I'm very new to MVVM and even WPF to some degree so bear with me...
I've got a MVVM application that has a main window, containing a viewmodel instance of different types depending on application state. One of these viewmodels is an options screen which contains a button to restart the application and log into the database as a different user. Using RelayCommand, how can I have the parent, (the main window) handle this command and issue a Window.Close() method call?
Define the RelayCommand on the parent view model. The Associated handlers will also be defined in the parent view model.
Now, when you creat the child view module pass the Paremtn View model object in to the Child ViewModel in the constructor (Dependency Injection Pattern).
Now you can set up the command binding for your view or view model.
Te other alternative would be to actually use a routed command instead of the relay command and let it bubble up to you parentview model.

MVVM and window lists

I'm writing an app that lets users browse through data, and I want to use the FireFox UI style: allow the user to open as many windows as they want, each with as many tabs as they want. I also want to try to do this using the Model-View-ViewModel pattern as much as possible.
Opening a new tab should be easy enough to handle in MVVM. Make an ObservableCollection of TabViewModel, bind that collection to the ItemsSource of a TabControl, and then opening a new tab is theoretically as easy as adding a new TabViewModel to the collection.
Here's the question that interests me: Is there a way to do the same thing for opening a new window? I.e., databind an ObservableCollection of WindowViewModel to the ItemsSource of... the Application's Windows collection?... so that when I add a new WindowViewModel to the observable collection, a new window automatically opens? And then tie that into app startup, so that instead of setting StartupUri, I just add the first WindowViewModel to the collection?
Since I can't actually databind Application.Windows, what would be the best way for the ViewModel layer to:
Add a new WindowViewModel and have a new Window appear automatically;
Remove the WindowViewModel and have its Window automatically close.
Remove the WindowViewModel from the collection if the user closes the window.
I could write my own object that watches an INotifyCollectionChanged and opens/closes windows in response to collection events, but I'm not sure whether that's the best way -- and if it is, I'm not sure of the best way to hook it into the application. Anyone have thoughts on the best way to go about this?
The point of MVVM is, that the ViewModel doesn't have to concern itself (in detail) with how the View will react to changes in the ViewModel.
One possibility would be a simple tracking algorithm in the View listening to the CollectionChanged event in the ViewModel, creating and destroying Windows on the go:
Dictionary<WindowModel, WindowView> _cache = new;
void WindowModelListChangedHandler(sender, args) {
switch(args.Action) {
case Add:
_cache[args.NewItem] = new WindowView(args.NewItem);
_cache[args.NewItem].Show();
break;
case Remove:
// ...
}
}

Resources