Dot Net For All

WPF MVVM Practical Data Application

In this article I will discuss about the WPF MVVM(Model-View-View Model) design pattern. I will create a simple data driven live application in WPF which will use MVVM pattern. I have used C# language and Visual Studio 2015 Community to develop the application. I have also used Entity Framework to interact with data base. First I will discuss about the MVVM and its uses. Then I develop a simple CRUD application using the MVVM concepts.

What is MVVM

MVVM is a simple design pattern which keeps the logical layers of application separate from each other.

WPF MVVM Application Design

Coming to the various parts of the WPF MVVM lets discuss them one by one here:

View – 

View Model- 

Model

Practical Application using WPF MVVM

In this example I will create a simple application which can be used to display, add, update or delete data. The UI for the application is as shown below.

WPF MVVM Application

The project solution looks like as shown below.

I will discuss all these parts here one by one . I have used Entity Framework to talk to database. You can visit my two articles to get starting with EF.

View

My MainWindow.xaml.cs is the main container of the sub View that is the PersonCollection.xaml. The code for the MainWindow.xaml is as shown below

<Window x:Class="MVVMApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MVVMApplication"
        xmlns:VM="clr-namespace:MVVMApplication.ViewModel"
        xmlns:View="clr-namespace:MVVMApplication.View"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <VM:MainWindowViewModel></VM:MainWindowViewModel>
    </Window.DataContext>
    <Grid>
        <View:PersonCollection></View:PersonCollection>
    </Grid>
</Window>

In the above code you can see at row number 11 that I am assigning data context for this view. The data context is the MainWindowViewModel.cs class, which is discussed in the View Model section.

and the code behind is

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            (this.DataContext as MainWindowViewModel).ShowMessageBox += delegate (object sender, EventArgs args)
            {
                MessageBox.Show(((MessageEventArgs)args).Message);
            };
        }
    }

As you can see there is not much code in the code behind file. Apart from the handler assignment to an event named ShowMessageBox which I will discuss later.

The sub view is the PersonCollection.xaml. The code is as shown below figure.

PersonCollection.xaml

In the figure I have marked all the data bindings with black arrow. Since I have set the DataContext of the main windows as the instance of MainWindowViewModel.cs class. For PersonCollection.xaml the view model is the same instance. This view model contains the properties named PersonCollection and SelectedPerson. It also contains the Add, Save and Delete commands. But as TextBox are bound to the FirstName, LastName, CityOfResidence and Profession properties, which are properties of Person class. I have set the datacontext of the Grid as SelectedPerson which contains all these properties. SelectedPerson property is of type Person class.

View Model

The view model class is as shown below.

 public class MainWindowViewModel:NotificationClass
    {
        Business _business;
        private Person _person;
        public EventHandler ShowMessageBox = delegate { };
        public MainWindowViewModel()
        {          
            _business = new Business();
            PersonCollection = new ObservableCollection<Person>(_business.Get());
        }


         private ObservableCollection<Person> personCollection;
         public ObservableCollection<Person> PersonCollection
         {
            get { return personCollection; }
            set { personCollection = value;
                  OnProprtyChanged();
                }
        }

        public Person SelectedPerson
        {
            get
            {
                return _person;
            }
            set
            {
                _person = value;
                OnProprtyChanged();
            }
        }


        public RelayCommand Add
        {
            get
            {
                return new RelayCommand(AddPerson, true);
            }        
        }

        private void AddPerson()
        {
            try
            {
                SelectedPerson = new Person();                       
            }
            catch (Exception ex)
            {
                ShowMessageBox(this, new MessageEventArgs()
                {
                    Message = ex.Message
                });
            }            
        }

        public RelayCommand Save
        {
            get
            {
                return new RelayCommand(SavePerson, true);
            }
        }

        private void SavePerson()
        {
            try
            {
                _business.Update(SelectedPerson);
                ShowMessageBox(this, new MessageEventArgs()
                {
                    Message = "Changes are saved !"
                });
            }
            catch (Exception ex)
            {
                ShowMessageBox(this, new MessageEventArgs()
                {
                    Message = ex.Message
                });
            }               
          
        }

        public RelayCommand Delete
        {
            get
            {
                return new RelayCommand(DeletePerson, true);
            }
        }

        private void DeletePerson()
        {
            _business.Delete(SelectedPerson);
        }
    }

It contains instance of the Business class as well as the Person(Model) class. All the properties bound to the View are present here. Commands are also present in the Model class which are of type RelayCommand which I have discussed later in the article.This class contains a reference to the Business class and a property of type Person (model) named as SelectedPerson. Business class allows us to interact with data source and contains all the business logic.

Model

Person is the model class. Business class has the logic to populate the Model class and interact with View Model. PersonDB is the class which contains logic to interact with the Entity Framework. The code for all of them is as follow.

 public class Business
    {
        PersonDB _dbContext = null;
        public Business()
        {
            AppDomain.CurrentDomain.SetData("DataDirectory",
             Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));
            _dbContext = new PersonDB();
        }

        internal IEnumerable<Person> Get()
        {
            return _dbContext.Person.ToList();
        }

        internal void Delete(Person person)
        {
            _dbContext.Person.Remove(person);
        }

        internal void Update(Person updatedPerson)
        {
            CheckValidations(updatedPerson);
            if (updatedPerson.Id > 0)
            {
                Person selectedPerson = _dbContext.Person.First(p => p.Id == updatedPerson.Id);
                selectedPerson.FirstName = updatedPerson.FirstName;
                selectedPerson.LastName = updatedPerson.LastName;
                selectedPerson.CityOfResidence = updatedPerson.CityOfResidence;
                selectedPerson.Profession = updatedPerson.Profession;
            }
            else
            {
                _dbContext.Person.Add(updatedPerson);
            }

            _dbContext.SaveChanges();
        }

        private void CheckValidations(Person person)
        {
            if(person == null)
            {
                throw new ArgumentNullException("Person", "Please select record from Grid or Add New");
            }

            if (string.IsNullOrEmpty(person.FirstName))
            {
                throw new ArgumentNullException("First Name", "Please enter FirstName");
            }
            else if (string.IsNullOrEmpty(person.LastName))
            {
                throw new ArgumentNullException("Last Name", "Please enter LastName");
            }
            else if ((int)person.Profession == -1)
            {
                throw new ArgumentNullException("Profession", "Please enter Profession");
            }
        }
    }
    public class Person:NotificationClass
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string CityOfResidence { get; set; }
        public Profession Profession { get; set; }
    }
  public class PersonDB:DbContext
    {
        public PersonDB():base("name=DefaultConnection")
        {
        }
        public DbSet<Person> Person { get; set; }
    }

There is not much business logic in this application. But I have put the validation of checking for null of emptiness of the fields as business logic.

Infrastructure

For infrastructure I have three classes as shown below

    public class NotificationClass : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        public void OnProprtyChanged([CallerMemberName]string propertyName = null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    public class RelayCommand : ICommand
    {
        private Action localAction;
        private bool _localCanExecute;
        public RelayCommand(Action action, bool canExecute)
        {
            localAction = action;
            _localCanExecute = canExecute;
        }


        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return _localCanExecute;
        }

        public void Execute(object parameter)
        {
            localAction();
        }
    }
   public enum Profession
    {
        Default = -1,
        Doctor,
        SoftwareEngineer,
        Student,
        SportsPerson,
        Other
    }

    public class MessageEventArgs:EventArgs
    {
        public string Message { get; set; }
    }

These are the classes which are building blocks of various functionalities. Notification class can be derived to any class which wants to use the INotifyPropertyChanged functionality.

RelayCommand class contains the logic to create and instance of ICommand interface. It binds to the Command of the controls in the View.

If I run the application and click on update button without doing any other action. I will get the pop up as shown below

Pop Up Using View Model

The popup is called using an event which is present in the View model. The handler for the event is present in the MainWindow.cs code behind.

If you want to Add New record, Click Add New Button > Enter All the details > Click Update.

If you want to Update a record. Select record in Grid > Update Details in TextBoxes >  Click Update Button

If you want to delete record. Select record in Grid > Click On Delete.

All the three layers are based on the principles which we have discussed earlier in the article. If you do any of the operations you will be able to see the result of the same in the application UI.

Though this application can be made more robust and intuitive. But for demo purpose I have added only basic functionalities.

Conclusion

In this article I have described about all the three components of WPF MVVM. The article also contains a working example of the CRUD application using WPF MVVM design principles.

References:

  1. WPF and MVVM: Advanced Model Treatment
  2. Practical MVVM

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview