A Simple WPF Application Implementing MVVM

Before starting with the project let me explain 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 to 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, and video.
3 Runs on Windows OS. Runs on Windows, Mac, and Linux.
4 Has a 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 the model to support changes in View.
  3. Very minor changes are 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 a student class with the following properties

Name,
Age,
JoiningDate

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MVVMDemo
{
    public class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime JoiningDate { get; set; }
    }
}

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 a notification whenever the property value is changed.

I created a ViewModelBase as below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace MVVMDemo
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

I also created a ViewModel class which inherits from ViewModelBase.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Collections.ObjectModel;
namespace MVVMDemo
{
    public class ViewModel : ViewModelBase
    {
        private Student _student;
        private ObservableCollection<Student> _students;
        private ICommand _SubmitCommand;
        public Student Student
        {
            get
            {
                return _student;
            }
            set
            {
                _student = value;
                NotifyPropertyChanged("Student");
            }
        }
        public ObservableCollection<Student> Students
        {
            get
            {
                return _students;
            }
            set
            {
                _students = value;
                NotifyPropertyChanged("Students");
            }
        }
        public ICommand SubmitCommand
        {
            get
            {
                if (_SubmitCommand == null)
                {
                    _SubmitCommand = new RelayCommand(param => this.Submit(), null);
                }
                return _SubmitCommand;
            }
        }
        public ViewModel()
        {
            Student = new Student();
            Students = new ObservableCollection<Student>();
            Students.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Students_CollectionChanged);
        }
        // Whenever new item is added to the collection, am explicitly calling notify property changed
        void Students_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            NotifyPropertyChanged("Students");
        }
        private void Submit()
        {
            Student.JoiningDate = DateTime.Today.Date;
            Students.Add(Student);
            Student = new Student();
        }
    }
}

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.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace MVVMDemo
{
    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute) : this(execute, null)
        {
        }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            
            _execute = execute;
            _canExecute = canExecute;
        }
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    }
}

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, ListView, and GridView. Many people can wonder why I am using ListView. Since WPF doesn't support GridView directly, I am using a ListView.

<Window x:Class="MVVMDemo.UserRegistrationView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:viewmodel="clr-namespace:MVVMDemo"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <viewmodel:ViewModel x:Key="ViewModel"/>
        <viewmodel:DatetimeToDateConverter x:Key="MyConverter"/>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource ViewModel}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Name" HorizontalAlignment="Center"/>
        <TextBox Grid.Row="0" Grid.Column="1" Width="100" HorizontalAlignment="Center" Text="{Binding Student.Name, Mode=TwoWay}"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Age" HorizontalAlignment="Center"/>
        <TextBox Grid.Row="1" Grid.Column="1" Width="100" HorizontalAlignment="Center" Text="{Binding Student.Age, Mode=TwoWay}"/>
        <Button Content="Submit" Command="{Binding SubmitCommand}" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="0"/>
        <ListView ItemsSource="{Binding Students}" Grid.Row="3" Grid.Column="1" Width="200">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="60"/>
                    <GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" Width="60"/>
                    <GridViewColumn Header="Joining Date" DisplayMemberBinding="{Binding JoiningDate, Converter={StaticResource MyConverter}}" Width="80"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</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 the date time value but I need to display only the date. I can do this either by String format in binding or else ValueConverter. I just want to go with the latter one, since you can understand ValueConverter also.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Windows.Data;
namespace MVVMDemo
{
    public class DatetimeToDateConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime date = (DateTime)value;
            return date.ToString("MM/d/yyyy");
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }
}

Now you can start debugging the application. I have attached the Source file also.