Codementor Events

Binding in View Models, wire up all the things!

Published Feb 03, 2023
Binding in View Models, wire up all the things!

Wire up those models

I recently worked with a client on a databinding issue in a WPF UI that was implementing the MVVM (Model View View Model) pattern. MVVM is amazing when everything is wired up properly, and that can be the challenge. There are a lot of moving parts in implementing the pattern and it is easy to miss something. If you do, things just won't work right. I have been fortunate to implement it in many systems, build libraries that help others use it, and to write articles and guidance. When I was the PM at Microsoft for patterns & practices Prism, MVVM was a central theme.

In this post I'll give a quick summary of MVVM and then give an example to illustrate how to use it in practice. The example is inspired by the issue I was working through with the client.

Why MVVM

The main reason for using this pattern is for implementing a UI that has complex logic in a maintainable way. It helps you to separate out UI concerns from the UI-centric business logic. With MVVM your code will be easier to test, and your UIs will be less brittle, at least from a standpoint of changes in the logic not breaking the UI or vice-versa.

Implementing MVVM

When you implement the pattern which is also know as Presentation Model, there are a number of core concepts. There also will be specific nuances and libraries that you will have to work with depending on the stack. I'll be focusing on how to do it in WPF for most of this post.

Concepts

View - The user interface. In the case of WPF it could be the window, frame, or other kind of component.

Model - The underlying data model that contains the information which will be displayed. Models may be just light data containers (anaemic) or have business methods. There is a lot of religious debate on this, which I'll avoid.

View Model - A UI-centric data model that the UI directly binds to. This contains members / logic that is specific to the UI context. Do not mix the View Model and the Model, that will just cause you problems. The view model's responsibilty is to offer up data that will surface in the view. It will talk to the underlying data, and format it in the way that makes sense for the UI. It will contain logic that responds to UI events. As an example, a view model may have a Refresh() method which is invoked by the UI to refesh the data. The view model is also providing encapsulation. It prevents the UI from having any intimate details or coupling to the underlying infrastructure and prevents UI details from bleeding down.

Data Binding - This is the glue that allows the view to connect to the view model in WPF. Other platforms like React have their own state and binding mechanisms. Same concept though it may play out differently.

Interfaces and Types

In WPF there a few different interfaces and types you need to know about to implement MVVM.

INotifyPropertyChanged - This interface (shorthand INPC) is implemented by a view model in order to notify the UI component that property has a changed. It contains a PropertyChanged event which is fired. If the prop value is a collection or a complex object, then the event is fired when the prop is updated with a new instance.

INotifyCollectionChanged - This interface (shorthand INCC) is implemented by a view model in order to tell the UI component that a collection property has had items added or removed from the list. The CollectionChanged event fires as part of this.

ObservableCollection - This collection implements INotifyCollectionChanged and will fire the event when it changes. This collection is very handly as it can wrap an existing collection. You will use it often when building UIs. Essentially it is a View Model.

Using it in practice, building a Contact Management app

Let's imagine a contact management system (for anyone who knows me from my ASP.NET Web API PM days they'll be laughing now). It has a screen that displays a list of contacts. You can filter contacts based on a contact name. It lets you add, delete, etc. You can also display the individual details of a contact.

Let's talk about the components. I'll focus on the view models specifically and leave it up to the reader to implement the View.

Below is a diagram illustrating the different parts. Disclaimer: I am a coder not a designer. 🤣

Screenshot 2023-02-02 at 7.44.26 PM.png

View

ContactListView

This displays the list of contacts. It has a text control for specifying the filter. The list displays first name, last name, and phone for each contact. It also has a "Save" and "Undo" buttons.

ViewModel - We'll have 2 corresponding models.

ContactViewModel

This view model is for supporting the display of an individual contact and ensuring changes are bubbled up.

public class ContactViewModel : INotifyPropertyChanged {
  private Contact _contact
  public ContactViewModel(Contact contact) {
    _contact = contact;
  }
    
  public string OnPropertyChanged(string property) {...}

  public First { 
    get { return _contact.First; }
    set { 
      _contact.First = value;
      OnPropertyChanged("First");
    }
  }
    
  // Last and Phone should be implemented similar to First. ID does not need to be displayed.
}

ContactListViewModel

The view model is for the UI business logic of the contact list view. The List and "Save" and "Undo" buttons are bound to the this. Notice how it calls to the data service to retrieve the contact info.

public class ContactListViewModel : INotifyPropertyChanged {	
  private IContactData _data;
  public ContactListViewModel(IContactService data) {
    _data = data;
  }
    
  public string OnPropertyChanged(string property) {...}
    
  private ObservableCollection<ContactViewModel> _contacts;
  public ObservableCollection<ContactViewModel> Contacts {
    get {
      return _contacts;
    }
    set {
      _contactList = value;
      OnPropertyChanged("Contacts");
    }
  }
    
  private string _filter;
  public string Filter {
    get {
      return _filter;
    }
    set {
      _filter = value;
            
      // Needed in case this is updated programatically
      OnPropertyChanged("Filter");
            
      //Update the list with the filtered data. Construct a ContactViewModel to wrap each contact.
      Contacts = _data.GetContacts(value).select((x)=>new ContactViewModel(x));
    }
  }
    
  public void Save() {
    _data.Save();
  }
    
  // No need to support undo at the data layer, just reload
  public void Undo() {
    if (_filter == "") {
      Contacts = Contacts = _data.GetContacts(value).select((x)=>new ContactViewModel(x));
    }
    else {
      Contacts = Contacts = _data.GetContacts().select((x)=>new ContactViewModel(x));
    }       
  }
    
  // Load the initital set of data
  public void Load() {
    Contacts = _data.GetContacts().select((x)=>new ContactViewModel(x));
  }
} 

Model

The model is a very simple data container.

public class Contact {
  public int ID {get;set;}
  public string First {get;set;}
  public string Last {get;set;}
  public string Phone {get;set;}
}

Data

The ContactService is responsible for retrieivng the contacts. It implements an interface to allow it to be easily mocked / injected by an IoC container.

public interface IContactService {
  IQueryable<Contact> GetContacts()
  IQueryable<Contact> GetContacts(string filter)
}

// Talks to some data source to return a Queryable<Contact>. Also handles persisting the data.
public class ContactService : IContactService {
  IQueryable<Contact> GetContacts() {
    ...
  }
    
  IQueryable<Contact> GetContacts(string filter) {
    ...
  }

  // Write the data
  public void Save() {
    ...
  }
}

How it all works

Now that we've described all the components and everything is correctly wired, let's talk about how it all works.

Loading data

  • The View is instantiated.
  • The ContactListViewModel is constructed with the ContactService injected
  • The Load() method is invoked to populate the data. It calls to the ContactService
  • The resulting data is wrapped in an ObservableCollection(ContactViewModel) using a Select() to create view models.
  • The PropertyChanged event is fired to tell the View that the Contacts property has changed.
  • UI displays the updated contacts and starts tracking CollectionChanged events. The UI also binds to each ContactViewModel instance in the collection to track `PropertyChanged' events for each contact.

Filtering data

  • The filter property is updated in the UI.
  • Databinding updates the Filter property on the ContactListViewModel
  • The view model invokes the ContactService.GetContacts method passing in the filter.
  • The result is wrapped similarly as during Load().

Saving data

  • ContactService.Save() is invoked. There's a lot of handwaviness here as I have left the implementation up to the reader such as using Entity Framwork.

Undo

  • The ContactService is invoked to reload the data.

Tips

Back to the opener of this post, there are a number of things that have to be wired up properly. Here's a few things to remember which may help you in your implementation.

  • The View should always invoke the ViewModel for logic. It should never call to any of the underlying classes.
  • If you are exposing a collection of objects (like the Contact List) you should seriously consider wrapping them in a View Model so that the UI will detect changes.
  • View Model setters will often be used for triggering logic. In the ContactManager case we used a setter on the Filter property to force the data to reload.
  • INotifyPropertyChanged is used for notifying the UI when individual properties are updated. Having an OnPropertyChanged method is a common convention for firing the event. This was called within the setter for Filter and Contacts.
  • INotifyCollectionChanged is used for notifying the UI when items are added or deleted to a collection. If the collection instance itself is changed, then the PropertyChanged event must be fired for the UI to detect it.
  • Breakpoints and the watch window are your friend. When trying to debug MVVM, put breakpoints in your MVVM code methods and properties and you can see exactly what is going on.

The MVVM Toolkit

There's a lot of boilerplate like INPC that you have to implement as part of the MVVM pattern. Years ago I was a big fan of the famed MVVM Light Toolkit written by my friend Laurent Bugnion. Although that is no longer maintained, Microsoft has released the MVVM toolkit. It contains helper classes like ObserableObject<T> which implement INPC for you and get ride of some of the boilerplate.

Summary

The MVVM pattern is a fantastic pattern for building UI applications and it has many benefits including making your code easy to maintain and test. Implementing MVVM can be easy to mess up. Hopefully this post will help you as you delve deeper into using it.

If you are struggling using MVVM, I am here, reach out!

Discover and read more posts from Glenn Block (he/him)
get started
post commentsBe the first to share your opinion
Show more replies