Click here to Skip to main content
15,867,885 members
Articles / Desktop Programming / WPF

WPF Master Details MVVM Application

Rate me:
Please Sign up or sign in to vote.
4.90/5 (61 votes)
13 Jan 2018CPOL8 min read 181.6K   111   28
WPF Master Details MVVM Application

It is highly recommended that you walk through the explanations by looking at the source code as it will help you gain a more solid understanding. You can also run the application on your computer. The goal of this project is to show you how to build a flexible and extensible application by using a very simple approach.

This is a WPF Master Detail application using the MVVM pattern at the UI tier. For now, let's just see what the application looks like.

There are a list of customers and each customer can have multiple orders. When you click on a customer, it shows you the list of orders belonging to that customer:

Image 1

You can add, edit, or delete customers by clicking on the buttons. The Add and Edit Customer window uses the same View so they look the same. Deleting the customer will delete all the orders belonging to that customer:

Image 2

Similarly, you can add, edit, or delete an order for a customer. The Add and Edit Order window also uses the same View. There are validations to make sure the order information entered is correct. The button is enabled only after it passes the validations.

Image 3

The UI tier is built using WPF MVVM pattern. It shows you how to:

  • Design the View and the ViewModel
  • Handle the user interactions
  • Perform validation
  • Open new windows using the MVVM pattern with an example of using Dependency Injection

The View and the ViewModel

Below is the diagram that shows you the main classes of the View and the ViewModel:

Image 4

In the View, we have:

  • MainWindow -- the main window when the application starts
  • CustomerListView -- lists all the customers, and can open the CustomerView when clicked on the Add Customer or Edit Customer
    • CustomerView -- the view to Add Customer or Edit Customer
  • OrderListView -- lists all the orders belonging to the selected customer, and can open the OrderView when clicked on the Add Order or Edit Order
    • OrderView -- the view to Add Order or Edit Order

The ViewModel classes are designed for binding to the views. These classes are not meant to replicate the domain model which is already done in the Business tier.

The MainWindow initializes the DataContext for the application by assigning it to the CustomerListViewModel singleton:

Image 5

In the XAML of the MainWindow, it specifies the Views to use (CustomerListView and OrderListView) by using DataTemplates:

Image 6

Notice that the CustomerListView binds to the CustomerListViewModel, and the OrderListView binds to the CustomerListViewModel's SelectedCustomer property, which is a CustomerViewModel that has a reference to the orders belonging to the customer. Below is the UI of the CustomerListView:

Image 7

The CustomerListView binds to the CustomerListViewModel, and we specify the properties of the ViewModel that we want each UI control to bind:

Image 8

Notice that the CellTemplate of the GridViewColumn defines the Edit and the Delete buttons. But more importantly, the SelectedItem of the ListView is bound to the SelectedCustomer of the ViewModel, which shows the list of orders belonging to the customer when the user clicks on the customer. This is because OrderListView is also bound to the SelectedCustomer of the ViewModel:

Image 9

Below is the XAML of the OrderListView:

Image 10

The button checks to see if the DataContext is null (when no customer is selected), if it is null then the Add Order button is disabled. The ListView binds to the Orders property of the CustomerViewModel to show the list of orders.

The CustomerView window is opened when you click on the Add Customer or the Edit Customer button. Below is the UI of the CustomerView which can be an Add Customer or an Edit Customer window:

Image 11

The window determines if it is an Add or the Edit window is by looking at the Mode property of the CustomerViewModel. The Mode can be either an Add or an Edit:

Image 12

The CustomerView displays different texts by looking at the Mode property:

Image 13

Clicking on the Add or Save button on the window will call the Update() method of the CustomerViewModel, which determines if it should add or edit a customer based on the Mode property:

Image 14

The same concept is applied to the OrderView.

Execute the Commands

To execute the commands, we write a CommandBase base class that we can use in the ViewModel. The base class merely passes the delegate that will be run when the command is executed:

Image 15

Then in the ViewModel, we declare the commands that are available for the View to bind. For example, in the CustomerViewModel, we define the command to delete the customer:

Image 16

Which will run the Delete() method when called. Then in the CustomerListView, we specify the command to execute when the delete button is clicked:

Image 17

All of the commands are set up this way to facilitate the binding of the commands to the ViewModel.

Setup Automatic UI Update

Next, we need to make sure the entire UI is updated when a property of the ViewModel is changed. This is done by including a ViewModelBase class in which all the ViewModels inherit:

Image 18

For example, the CustomerViewModel inherits from the ViewModelBase class:

Image 19

When the FirstName property of the CustomerViewModel changes (such as when editing a customer), the entire UI should reflect such change. In the ViewModel's property, we just call the OnPropertyChanged method with the property name and WPF will take care of the rest:

Image 20

Undo After an UI Update

When a user cancels a change, you will encounter an issue with data binding. For example, the user may change the first name of a customer in the edit customer window which will update the entire UI...

Image 21

...then later decide to cancel by pressing the Cancel button. With the default two-way data binding, the UI will retain the changed value even though the user canceled the change.

The first thought may be to use one-way binding and commit the change only if the user clicks on the Save button. This will work except you will encounter a few more issues:

  • You will need to pass multiple values to the save command, which clutters up the XAML and you will also need to cast the multi-value being passed from the command from a list of type object.
  • You will not be able to use the validation provided by WPF which requires data binding.

The way to solve this is to let WPF proceed with the binding and do a rollback when the cancel button is clicked. We store the original value using MemberwiseClone() in the constructor and the Update() method of the ViewModel (although you can write your own deep copy methods if you have reference type fields):

Image 22

Then the CancelCommand in the ViewModel will just call Undo() and rollback to the original value and the entire UI will be updated:

Image 23

The same method is applied to the Edit Order screen.

Validation

Below is the OrderView where the Save button is disabled with the validation message:

Image 24

The button is disabled based on the Validation.HasError property of the textboxes in the OrderView.xaml:

Image 25

 

To perform the validation on the textboxes, we set the ValidatesOnDataErrors, NotifyOnValidationError, and UpdateSourceTrigger properties:

Image 26

The PropertyChanged value of the UpdateSourceTrigger means that validation is performed on every keystroke in the textbox. We define the UI to show when an error happens in ValidationStyle.xaml:

Image 27

The AdornedElementPlaceholder shows the control being validated, and the ErrorContent is the validation message string.

The validation check is done in the ViewModel by implementing the IDataErrorInfo interface:

Image 28

In the implementation of the IDataErrorInfo interface, we specify the message to display based on the condition:

Image 29

Notice that we set the Quantity property of the OrderViewModel to string instead of int, this is so that we can display a more user friendly message when a non-integer is entered instead of the default message on casting which is not very intuitive to a regular user.

Open New Window

There are 2 ways to open a new window:

  • Use the Code-Behind
  • Use the ViewModel via Dependency Injection

Using the code-behind creates less code, while using the ViewModel lets you abstract out the details on opening of a window by using an established framework.

Open Window Using the Code Behind

We open the Add/Edit Order window using the code-behind by assigning the DataContext to the new window and open it:

 

Image 30

Open Window Using the ViewModel via Dependency Injection

Dependency Injection means you pre-register a type to an interface, and when the interface is requested, the pre-registered type is automatically created for the interface. This is shown in the opening of the Add/Edit Customer window. In the CustomerViewModel, we define the command to open the Edit Customer window, which opens an IModalDialog:

Image 31

Notice that the ViewModel does not know what is the View being opened. It only needs to get the reference to the pre-registered IModalDialog interface and call its methods to open the window.

The pre-registration of the View to the IModalDialog is done in the BootStrapper when the application starts. In this case, we use the CustomerViewDialog as the IModalDialog when the interface is requested:

Image 32

Also notice that we use the UnityServiceLocator for managing the registrations, underneath the cover it simply uses the Unity Framework from the Microsoft Enterprise Library (you can use other frameworks if you like):

Image 33

We hope you find this project useful. Although there are a lot more things you can do, this should give you a good start on how to build a flexible master details application.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionLink Pin
Member 126555561-Mar-21 20:11
Member 126555561-Mar-21 20:11 
QuestionGood article, but the link is not correct Pin
fantamen4-Aug-20 6:21
professionalfantamen4-Aug-20 6:21 
QuestionGreat article, but the link doesn't work Pin
K.Yamaguchi25-Jun-20 21:38
K.Yamaguchi25-Jun-20 21:38 
QuestionGreat article, except... Pin
James Lonero17-Jan-18 7:45
James Lonero17-Jan-18 7:45 
QuestionMost comprehensive article Pin
tinomclaren1-Aug-17 19:37
tinomclaren1-Aug-17 19:37 
QuestionNice article Pin
Manoj Kumar Choubey1-Mar-16 0:04
professionalManoj Kumar Choubey1-Mar-16 0:04 
QuestionLink don't work Pin
Member 116588042-May-15 1:32
Member 116588042-May-15 1:32 
AnswerRe: Link don't work Pin
Member 1135294318-Sep-15 5:22
Member 1135294318-Sep-15 5:22 
AnswerRe: Link don't work Pin
MarkWardell3-Oct-15 10:09
MarkWardell3-Oct-15 10:09 
QuestionLoad new user control to mainWindow Pin
prwijerathne30-Mar-15 2:03
prwijerathne30-Mar-15 2:03 
QuestionMessageBox.Show ? Pin
chaimf21-Sep-14 6:02
chaimf21-Sep-14 6:02 
GeneralMy Vote of 5 Pin
drctrpblc28-Mar-14 8:18
drctrpblc28-Mar-14 8:18 
GeneralMy vote of 5 Pin
skel_3633-Jul-13 10:56
skel_3633-Jul-13 10:56 
GeneralMy vote of 5 Pin
justinli1219-Mar-13 2:51
justinli1219-Mar-13 2:51 
QuestionNice Example Pin
Doug J. Huras8-Mar-13 4:42
Doug J. Huras8-Mar-13 4:42 
QuestionHow to publish? Pin
Member 89230787-Mar-13 4:09
Member 89230787-Mar-13 4:09 
QuestionUsing this as a reference, have questions please help.... Pin
ajk82513-Jan-13 20:37
ajk82513-Jan-13 20:37 
AnswerRe: Using this as a reference, have questions please help.... Pin
David Sherwood16-Jan-18 13:21
David Sherwood16-Jan-18 13:21 
GeneralMy vote of 5 Pin
Daskul7-Dec-12 16:16
Daskul7-Dec-12 16:16 
Questionquestion Pin
9o9o27-Nov-12 22:38
9o9o27-Nov-12 22:38 
GeneralMy vote of 5 Pin
MichaelMM11-Sep-12 3:39
MichaelMM11-Sep-12 3:39 
GeneralMy vote of 5 Pin
Nikhil_S26-Jul-12 20:37
professionalNikhil_S26-Jul-12 20:37 
Nice one.
GeneralMy vote of 5 Pin
Sandeep Mewara11-Jun-12 1:15
mveSandeep Mewara11-Jun-12 1:15 
SuggestionCheck out this example: http://www.codearsenal.net/2012/05/mvvm.html Pin
JohnDetroit5-Jun-12 22:02
JohnDetroit5-Jun-12 22:02 
GeneralNice one Pin
thatraja20-Feb-12 8:18
professionalthatraja20-Feb-12 8:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.