A Simple WPF Application Implementing MVVM

Before starting with the project let me explain about WPF, Silverlight and MVVM for those who are new to those areas.

WPF

  1. Short form for Windows Presentation Foundation.
  2. Next generation of Windows applications / Winforms.
  3. Uses XAML for UI.
  4. Supports media, documents and graphics.

Silverlight

  1. Cross-platform, cross-browser platform for delivering rich, interactive applications.
  2. Commonly referred as a rival to Adobe Flash.
  3. Uses XAML for UI.
  4. Sub-set of WPF and can be used only in web applications.

Difference between WPF and Silverlight

S.No WPF Silverlight
1 Targets Desktop. Targets Browsers.
2 Has 3D graphics. Supports rich online media such as streaming audio, video.
3 Runs on Windows OS. Runs on Windows, Mac and Linux.
4 Has full .net framework. Limited .Net framework.

MVVM

In simple words,
View knows ViewModel,
ViewModel knows Model,
But not vice versa

Why MVVM

  1. Easy to unit test.
  2. No need to change model to support changes in View.
  3. Very minor changes required in ViewModel to support changes in View.
  4. Separates UX designer and developer i.e the development team can focus on creating ViewModel classes, and the design team can focus on making user-friendly Views.

Simple WPF application using MVVM

Here I am just going to get a student name and age from the user and display the details in a GridView as in the figure.

The important areas covered are MVVM, using Relay Command and IValueConverter.

Creating a Student class

I created student class with the following properties,

Name,
Age,
JoiningDate
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace MVVMDemo  
  7. {  
  8.     public class Student  
  9.     {  
  10.         public string Name { getset; }  
  11.         public int Age { getset; }  
  12.         public DateTime JoiningDate { getset; }  
  13.     }  
  14. }  

 

Creating ViewModelBase and ViewModel class

It's always a good idea to have a ViewModelBase class and inherit all the ViewModels from that. Hence we can reuse the code for implementing INotifyPropertyChanged.

The main purpose of using INotifyPropertyChanged is to get notification whenever the property value is changed.

I created a ViewModelBase as below.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ComponentModel;  
  6.    
  7. namespace MVVMDemo  
  8. {  
  9.     public class ViewModelBase:INotifyPropertyChanged  
  10.     {  
  11.         public event PropertyChangedEventHandler PropertyChanged;  
  12.         protected void NotifyPropertyChanged(string propertyName)  
  13.         {  
  14.             if (PropertyChanged!=null)  
  15.             {  
  16.                 PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  
  17.             }  
  18.         }  
  19.     }  
  20. }  
I also created a ViewModel class which inherits from ViewModelBase.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Windows.Input;  
  6. using System.Collections.ObjectModel;  
  7.   
  8. namespace MVVMDemo  
  9. {  
  10.     public class ViewModel : ViewModelBase  
  11.     {  
  12.         private Student _student;  
  13.         private ObservableCollection<Student> _students;  
  14.         private ICommand _SubmitCommand;   
  15.         public Student Student  
  16.         {  
  17.            get  
  18.             {  
  19.                 return _student;  
  20.             }  
  21.            set  
  22.             {  
  23.                 _student = value;  
  24.                 NotifyPropertyChanged("Student");  
  25.             }  
  26.         }  
  27.         public ObservableCollection<Student> Students  
  28.         {  
  29.            get  
  30.             {  
  31.                 return _students;  
  32.             }  
  33.            set  
  34.             {  
  35.                 _students = value;  
  36.                 NotifyPropertyChanged("Students");  
  37.             }  
  38.         }  
  39.         public ICommand SubmitCommand  
  40.         {  
  41.            get  
  42.             {  
  43.                 if (_SubmitCommand == null)  
  44.                 {  
  45.                     _SubmitCommand = new RelayCommand(param => this.Submit(),  
  46.                         null);  
  47.                 }  
  48.                 return _SubmitCommand;  
  49.             }  
  50.         }   
  51.         public ViewModel()  
  52.         {  
  53.             Student = new Student();  
  54.             Students = new ObservableCollection<Student>();  
  55.             Students.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Students_CollectionChanged);              
  56.         }  
  57.         //Whenever new item is added to the collection, am explicitly calling notify property changed  
  58.         void Students_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)  
  59.         {  
  60.             NotifyPropertyChanged("Students");  
  61.         }          
  62.         private void Submit()  
  63.         {  
  64.             Student.JoiningDate = DateTime.Today.Date;  
  65.             Students.Add(Student);  
  66.             Student = new Student();  
  67.         }  
  68.     }  
  69. }  
RelayCommand

Since we are following the MVVM pattern, instead of having a Button click event in code behind, we are going to use the Command Property of the button. The RelayCommand allows you to inject the command's logic via delegates ed into its constructor. This approach allows for terse, concise command implementation in ViewModel classes. RelayCommand is a simplified variation of the DelegateCommand.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Windows.Input;  
  6.    
  7. namespace MVVMDemo  
  8. {  
  9.     public class RelayCommand : ICommand  
  10.     {  
  11.         public RelayCommand(Action<object> execute): this(execute, null)  
  12.         {  
  13.         }  
  14.         public RelayCommand(Action<object> execute, Predicate<object> canExecute)  
  15.         {  
  16.             if (execute == null)  
  17.                 throw new ArgumentNullException("execute");  
  18.             _execute = execute;  
  19.             _canExecute = canExecute;  
  20.         }  
  21.         public bool CanExecute(object parameter)  
  22.         {  
  23.             return _canExecute == null ? true : _canExecute(parameter);  
  24.         }  
  25.         public event EventHandler CanExecuteChanged  
  26.         {  
  27.             add { CommandManager.RequerySuggested += value; }  
  28.             remove { CommandManager.RequerySuggested -= value; }  
  29.         }  
  30.         public void Execute(object parameter)  
  31.         {  
  32.             _execute(parameter);  
  33.         }  
  34.         private readonly Action<object> _execute;  
  35.         private readonly Predicate<object> _canExecute;  
  36.     }  
  37. }  
View

Now we have to create a view and bind it with ViewModel properties. The code for the view can be found below. I have used a window, Grid, Textbox, Textblock, Button, List View and GridView. Many people can wonder why I am using ListView. Since WPF doesn't support GridView directly, I am using a ListView.
  1. <Window x:Class="MVVMDemo.UserRegistrationView  
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  3. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
  5.         xmlns:viewmodel="clr-namespace:MVVMDemo"  
  6.     Title="Window1" Height="300" Width="300">  
  7.     <Window.Resources>  
  8.         <viewmodel:ViewModel x:Key="ViewModel"/>  
  9.         <viewmodel:DatetimeToDateConverter x:Key="MyConverter"/>  
  10.     </Window.Resources>  
  11.     <Grid DataContext="{Binding Source={StaticResource ViewModel}}">  
  12.         <Grid.RowDefinitions>  
  13.             <RowDefinition Height="Auto"/>  
  14.             <RowDefinition Height="Auto"/  
  15.             <RowDefinition Height="Auto"/>  
  16.             <RowDefinition Height="Auto"/>  
  17.             <RowDefinition Height="*"/>  
  18.        </Grid.RowDefinitions>  
  19.         <Grid.ColumnDefinitions>  
  20.             <ColumnDefinition Width="Auto"/>  
  21.             <ColumnDefinition Width="*"/>  
  22.         </Grid.ColumnDefinitions>  
  23.         <TextBlock Grid.Row="0" Grid.Column="0" Text="Name" HorizontalAlignment="Center"/>  
  24.         <TextBox Grid.Row="0" Grid.Column="1" Width="100" HorizontalAlignment="Center" Text="{Binding Student.Name, Mode=TwoWay}"/>  
  25.         <TextBlock Grid.Row="1" Grid.Column="0" Text="Age" HorizontalAlignment="Center"/>  
  26.         <TextBox Grid.Row="1" Grid.Column="1" Width="100" HorizontalAlignment="Center" Text="{Binding Student.Age, Mode=TwoWay}"/>  
  27.         <Button Content="Submit" Command="{Binding SubmitCommand}" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="0"/>  
  28. <ListView ItemsSource="{Binding Students}" Grid.Row="3" Grid.Column="1" Width="200">  
  29.     <ListView.View>  
  30.         <GridView>  
  31.             <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="60"/>  
  32.             <GridViewColumn  Header="Age" DisplayMemberBinding="{Binding Age}" Width="60"/>  
  33.             <GridViewColumn  Header="Joining Date" DisplayMemberBinding="{Binding JoiningDate, Converter={StaticResource MyConverter}}" Width="80" />  
  34.         </GridView>  
  35.      </ListView.View>  
  36. </ListView>  
  37.            </Grid>   
  38. </Window>  
If you see the above XAML code, you can see I am using Windows resources in my view. In resources, I am adding a reference to my ViewModel and DateTimeToDateConverter.

ValueConverter

We might often get some values from the property but we have to display some other value in the View. Say for example in my project, the Joining date is date time value but I need to display only date. I can do this either by String format in binding or else ValueConverter. I just want to go with latter one, since you can understand ValueConverter also.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Globalization;  
  6. using System.Windows.Data;  
  7.   
  8. namespace MVVMDemo  
  9. {  
  10.     public class DatetimeToDateConverter : IValueConverter  
  11.     {  
  12.         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
  13.         {  
  14.             DateTime date = (DateTime)value;  
  15.             return date.ToString("MM/d/yyyy");  
  16.         }  
  17.         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
  18.         {  
  19.             return value;  
  20.         }  
  21.     }  
  22. }  
Now you can start debugging the application. I have attached Source file also.