MVVM scenario with main window and many user controls - wpf

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).

Related

WPF: DataGrid does not show records unless its ItemsSource property explicitly bound to the list of records

I have the following issue in my WPF application. I have a dedicated area in my Main window where I want to display various user controls, depending on the user's input. One of the UCs I have will allow the user to search the repository for some records. The customer's request is that the UC should contain all records when displayed the first time and only filter the data based on search criteria for subsequent searches. I am using the MVVM patter and I have my views, my models (defined in a separate Business Layer project) and my view-models.
I inject the repository service in my UC's view model, so I can fetch the data any time the user performs a search.
I set the the DataContext of my view to the instance of the view-model I created. Here is an excerpt from the Initialize method of my MainWindowviewModel class:
_myUserControlView = new MyUserControlView();
var userControlViewModel = new MyUserControlViewModel(_repositoryService);
_myUserControlView.DataContext = userControlViewModel;
The user control view contains a grid that is supposed to display the records fetched by the repo service. When the view-model for my UC view gets built, I can see that the repo service is fetching the correct data. But nothing is displayed in the grid. The XAML contains the proper ItemSource binding for the grid:
<DataGrid Name="MyUcDataGrid" AutoGenerateColumns="True" Height="Auto" Width="Auto" ItemsSource="{Binding MyRepoFetchedList}">
However, if I explicitly set the ItemsSource in my Initialize method, the data is properly displayed:
_myUserControlView = new MyUserControlView();
var userControlViewModel = new MyUserControlViewModel(_repositoryService);
_myUserControlView.DataContext = userControlViewModel;
_myUserControlView.MyUcDataGrid.ItemsSource = userControlViewModel.MyRepoFetchedList;
I was following some courses on PluralSight and none of them had this explicit assignment. For all those working examples, the binding in the XAML was enough.
What am I missing? My models are defined in a BL library, but I do not think that is the case. The samples on PluralSight, more simpler than my solution, would have the models in the same WPF project. The other thing is that the example I saw on PluralSight would fetch the records asynchronously. My code, still in the early development stages, does not. I will change that, but I don't think it is the case. Anyone has any clues why the DataGrid would not display my records unless I bind again the ItemsSource to the proper list in the code?
TIA,
Eddie

Update a DataGrid in MVVM

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.

WPF DataBinding with MVVM and User Controls

Let's say I have a Customer Window showing information about a customer, like Name, address and phone number. On the bottom there is a DataGrid of their orders. Of course the Customer has an Orders property, so if you're using MVVM, you would just set:
ItemsSource = "{Binding Customer.Orders}"
However, now let's say that the data grid is now part of a user control, which also includes controls for editing/adding/removing Orders. I want to use this same set of controls in multiple places, and I would like all the logic for editing/adding/removing Order objects to be encapsulated in the user control. And because I want to use commands, rather than event handlers, I would like the user control to have its own view model.
So now the question is: how do I pass the orders from the Customer view model to the Orders user control's view model? Because the Orders user control will be bound to a view model, I can't say:
<local:OrdersUserControl DataContext="{Binding Customer.Orders}" />
because the user control has it's own view model. It would expect to see Customer.Orders there, and of course it's not.
I guess this is kind of a chicken or the egg situation.
Your help is always appreciated.
Aaron
And now for my weekly "don't do that" answer...
I can't say ... because the user control has it's own view model.
To which I say
Creating a ViewModel for your UserControl is a code smell.
You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.
The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
MVVM doesn't mean no codebehind. Put your UI logic for your user control in the codebehind. If it is so complex that you need business logic inside the user control, that suggests it is too encompassing. Break it down into two or more.
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
There are many ways to skin a cat. The way I've been doing this is by having my CustomerViewModel have a property of type OrdersViewModel. Then in the constructor (or wherever you are setting the customer) have it set the "OrdersContext" with a new OrdersViewModel passing in the customer.orders.
XAML:
<local:OrdersUserControl DataContext="{Binding OrdersContext}" />
ViewModel:
public CustomerViewModel(Customer customer)
{
Customer = customer;
OrdersContext = new OrdersViewModel(customer.Orders);
}

Setting DataContext in Catel with WPF

So I've started looking at the Catel MVVM framework and it looks like it will solve a couple of problems I have encountered, but I have one really silly issue. I think I'm just missing something small.
I took one of my smaller WPF projects to switch over the Catel as a way for me to learn it. I have a simple 'Player Registration' form, with fields like name and surname. I recreated my original view model by using the vm codesnippet and all is good, all the properties and attributes I've set up as I've read in the documentation.
I then changed the UserControl I used for 'Player Registration' (PlayerRegistrationView) to a catel:UserControl. I placed PlayerRegistrationView on a standard WPF Window (nothing else, just a xmlns for the View and the view as the only content on the window, no attributes)
But here is my problem:
I have a MainWindow with a button on to open the Window for the player registration. The on-click event simply is this:
private void ButtonPlayerClick(object sender, RoutedEventArgs e)
{
var playerRegistration = new PlayerRegistrationDialog
{
Owner = this,
DataContext = new PlayerRegistrationViewModel(),
};
playerRegistration.Show();
}
Running my program and then clicking on the button results in an NotSupportedException on my PlayerRegistrationView: The view model of the view could not be resolved. Use either the GetViewModelType() method or IViewModelLocator
I tried making the ViewModel a static resource on the window and setting the datacontext there, but it produces the same error.
I am at a loss. What have I missed?
Thanks
The whole point of Catel is that it automatically wires up all the views and view models. The "complex" thing that you are trying to achieve is that you have a view which is placed on a window. You want the window to have the same data context as the view in order to do some stuff in the window as well.
In Catel, it is possible to place any view with datacontext management on a DataWindow (window in Catel). Then it will work like this:
DataWindow
|=> View
If the DataWindow and the View share the same view model type, then they share the same view model. For example:
PlayerRegistrationWindow => derives from catel:DataWindow
PlayerRegistrationView => derives from catel:UserControl
Since both start with PlayerRegistration, they will both be resolved to PlayerRegistrationViewModel automatically.
To show the window, the only thing you have to do is this:
var viewModel = new PlayerRegistrationViewModel();
var uiVisualizerService = ServiceLocator.Default.ResolveType<IUIVisualizerService>();
uiVisualizerService.Show(viewModel);
All will work automatically and you don't have to worry about setting any datacontext yourself.

Using MVVM show new window and get updates data

I'm working on a WPF MVVM application. I'm showing some data in a datagrid. I've two buttons to Add and Edit the selected record. I've data in ViewModel and I've to show another window (view) and make sure that ViewModels should have no information about views.
Where should I create its view and viewmodel?
How to get the data back and update datagrid?
How can I achieve this in MVVM?
We have not yet decided to use any framework, so I've to create my own interface.
Note: This ended up being quite a long answer - please ask me if anything is unclear
The implementation of dialog windows is a contentious issue in MVVM designs, and different people use different approaches.
Like you, I've decided not to use any framework and implement most things by hand. When it comes to dialog windows, I choose to be pragmatic about my implementation of MVVM, by launching the Dialog Window from inside my ViewModel. Also, I allow each Dialog ViewModel to have a reference to the Window it is displayed in, so it can close it when appropriate (details below). This breaks some of the strict MVVM "rules", but it gets the job done.
The main downside of this is that it might break unit testing if you are testing something that goes through a dialog. However, you can go a long way without running into that problem and it has not bothered me yet.
I've built up a bit of a library of dialog ViewModels which I can easily extend. It's way too much code to post here, but I'll show you the highlights.
Base ViewModel for Dialogs
Each of my dialog windows has a ViewModel that inherits from DialogViewModelBase, which is similiar to my regular ViewModelBase in that it provides support for INotifyPropertyChanged etc. The interesting part is this public method, which I call from wherever to launch the Dialog:
/// <summary>
/// Creates window instance for this dialog viewmodel and displays it, getting the dialog result.
/// </summary>
public void ShowDialogWindow()
{
// This is a property of the DialogViewModelBase class - thus, each DialogViewModel holds a reference to its own DialogWindow:
this.DialogWindow = new Dialogs.Views.DialogWindow();
// Tell the DialogWindow to display this ViewModel:
this.DialogWindow.DataContext = this;
// Launch the Window, using a method of the Window baseclass, that only returns when the window is closed:
this.DialogWindow.ShowDialog();
}
Window launched in the above method will close when its Window.DialogResult property is set. This is why the DialogWindow is a property of the DialogViewModelBase class - when the subclassing dialog ViewModel wants to close the dialog window, it simply sets the result:
protected void CloseDialogWithResult(bool dialogWindowResult)
{
// Setting this property automatically closes the dialog window:
this.DialogWindow.DialogResult = dialogWindowResult;
}
Host Window for Dialog Views
The Dialogs.Views.DialogWindow class that the ShowDialogWindow method instantiates is defined in XAML and is a subclass of Window. It has two important features. The first is that it's primary content element is simply a ContentControl that binds to the current context. This allows me to define different Views for different subclasses of DialogViewModelBase, and the DialogWindow will host the corresponding View based on the type of the context:
<ContentControl Content="{Binding}" /> <!-- In reality this is inside a border etc but its simplified here for demonstration -->
The second important feature of the DialogWindow XAML is that it defines which dialog Views go with which dialog ViewModels. Here is a sample:
<Window.Resources>
<!-- DEFAULT ViewModel-View TEMPLATES -->
<DataTemplate DataType="{x:Type dialogs:YesNoMessageBoxDialogViewModel}">
<views:MessageBoxView />
</DataTemplate>
<DataTemplate DataType="{x:Type dialogs:ErrorDialogViewModel}">
<views:ErrorDialogView/>
</DataTemplate>
</Window.Resources>
What all this does, is that I can define dialogs as subclasses to DialogViewModelBase and implement a View for each, and then tell DialogWindow which View its ContentControl must show for which dialog ViewModel.
Launching a Dialog and getting results
Below is a sample from one of my application ViewModels, in which I launch a Dialog Window that allows the user to select an Asset Type for creation:
public void CreateNewAsset()
{
// Instantiate desired Dialog ViewModel:
Dialogs.NewAssetTypeSelectionDialogViewModel dialog = new Dialogs.NewAssetTypeSelectionDialogViewModel();
// Launch Dialog by calling method on Dialog base class:
dialog.ShowDialogWindow();
// Execution will halt here until the Dialog window closes...
// The user's selection is stored in a property on the dialog ViewModel, and can now be retrieved:
CalculatorBase.AssetTypeEnum newAssetType = dialog.AssetType;
switch (newAssetType)
{
// Do stuff based on user's selection...
}
}
PS: I should really write a blog entry about this - when I do, I will post the link here, as the blog entry will probably have more complete code samples.
It depends how you are handling the data. I will assume that changes made in the popup window can be accepted only when user clicks something like save in other case they should be discarded.
So firstly, I would suggest using MVC approach as controller is perfect for such tasks. You build viewmodels in it, assign them o views and show the views. VM's simply keeps data and commands, commands execute methods are kept in controller. In other words you have singleton class which manages your VM's and views.
You should check out Prism framework. It offers great things like view regios where you can inject different user controls on the runtime, commanding and MVC layering out of the box alongside IOC and DI patterns.

Resources