Part 18 - Understanding MVVM: ObservableCollection and INotifyPropertyChanged

Play Part 18 - Understanding MVVM: ObservableCollection and INotifyPropertyChanged
Sign in to queue

Description

 In the previous lessons we learned about the Data Model and how it is used as the Data Context for Pages and Controls.  And when we talked about the Data Model, we’re talking specifically the classes in the SampleDataSource.cs file.  We learned about the binding syntax used by XAML to bind to the Data Model that was exposed by the code behind.

The Hub App Template employs a design pattern in software called MVVM, or Model-View-ViewModel.  At a high level, MVVM seeks to “separate concerns”, extracting code that is specific to presentation (the View, or rather, the XAML that defines the user interface), the code specific to the domain (the Model, or rather, the SampleDataGroup and SampleDataItem classes), and the code that coordinates the activities of the model and provides the view with access to the model, (the ViewModel, or rather, the SampleDataSource class).

MVVM is a slightly advanced topic, and frankly, I’m not sure I’m qualified to provide an exhaustive explanation.  Unfortunately, even some authors and books that have attempted to clarify this topic have not always done a good job doing it either.  But what I can do is show you the basics and how and why they are employed in the Hub App Template, so that you can begin to understand the thought process that influenced the organization and the design of the code in the template.  Obviously, my goal is to help you better make changes to this template, and a fundamental understanding of MVVM should help you build a mental model of the responsibilities for each class, each piece of the puzzle.

Let’s start with the Model.  In software architecture, a Model is a representation of a given business or domain problem.  If you were building an Accounting system, your Model might have classes for things like Account, Customer, Credit, Debit, and so on.  If you were building a game, your Model might have classes for things like Planet, Spaceship, Soldier, Weapon and so on.  If you were building a media app, your Model might have classes for Sound of Video, Categories of Sounds and videos, and so on.  In most MVVM implementations I’ve seen, the Model is little more than a data structure … a class defining properties and basically the “state” of the object, but almost no methods to implement business logic.  (Just to provide a little guidance here, many believe this is an anti-pattern, or rather, “a bad idea”, called an Anemic Domain Model in which business logic is typically implemented in separate classes which transform the state of the domain objects.  But we’ll ignore that for now.)  You can see examples of a typical model that utilizes MVVM in our Hub App Template: the SampleDataItem and SampleDataGroup classes.  These have properties, but no real methods the implement logic.

Next is the View.  The view has the responsibility of presentation of the Model.  It does this through Binding, a core component of XAML through the binding syntax we’ve seen.  What does the View bind to?  We’ve seen examples of binding to data via the ViewModel, but later in this series we’ll see how the View binds to commands that are implemented in the ViewModel.  Commands are essentially methods that perform logic and respond to the user’s interaction with the View.

The ViewModel is the key to making MVVM work.  The ViewModel provides a layer of abstraction between the View and the Model.  We can see this in the SampleDataSource class.  It provides a data source to the View.  It must load data into instances and collections of the Model classes to create an object graph.  When I use the term “object graph” I’m referring to instances of classes that are related.  We see this at work in the GetSampleDataAsync() method.  Then, once the object graph is loaded into memory, there are various ways of returning parts of it based on the needs of a given view.  So, that is the role of the public property Groups, as well as the methods GetGroupsAsync(),  GetGroupAsync() and GetItemAsync().

You might wonder whether the code behind (i.e., HubApp.xaml.cs) for the XAML is part of the View or the ViewModel.  After all, (1) it retrieves the appropriate data for the given View in the navigationHelper_LoadState() method, and (2) it exposes the data retrieved from the ViewModel as a property to the XAML (i.e., defaultViewModel).  I think of these are merely helpers to the main job of the View, and therefore I would classify them as part of the View itself.

I say all of that to say this … the View should merely observe the data exposed by the ViewModel.  In other words, it should watch the collections (or instances of objects) that were delivered by the ViewModel for changes and react to those changes.  This is called “observability”.  If an instance of an object that is part of a collection managed by the ViewModel is added, removed or updated, then the View should be notified of the change and the View should update itself with that change. 

Fortunately, all of the “plumbing” required to make this work is baked into XAML.  But from your perspective, as the developer in order to make this work, your classes must implement the INotifyPropertyChanged interface.  By implementing INotifyPropertyChanged, your class says “You can observe me, I’ll let you know when I change.”  I don’t have a complete example to show you, but let me show the simplest example I can think of to show how such an implementation might work:

class Sample : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  public int ID { get; set; }

  private string name = String.Empty;

  public string Name
  {
    get
    {
      return this.name;
    }

    set
    {
      if (value != this.name)
      {
        this.name = value;
        NotifyPropertyChanged(”Name”);
      }
    }
  }

  private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

By implementing INotifyPropertyChanged we must agree to raise an event called PropertyChanged.  This is the fundamental wiring that allows other classes (like user interface classes) to be notified that changes to an instance of this class have occurred (so now go update yourself to reflect those changes in your user interface).  This event is triggered in the NotifyPropertyChanged() method, which is called whenever the Set operation is invoked on the Name property.  So, in other words, when a line of code like this is executed:

Sample mySample = new Sample();
mySample.Name = “Bob”;

… the PropertyChanged event is fired and any class that listens to this event (like certain user interface classes / controls in the Phone API) will have an opportunity to refresh the data they display.

Having said all of that, the Hub App Template does not have any classes that directly implement INotifyPropertyChanged.  Instead, it uses ObservableCollection<T> (as well as a custom class, ObservableDictionary) which implements the INotifyPropertyChanged interface internally.  So, the moral to the story is that, as long as we’re using ObservableCollection<T> to expose collections of the objects from the ViewModel to the View, we’ll get observability at the COLLECTION level.  ObservableCollection<T> is essentially a List<T> with the added superpower of observability.

A couple of important aspects about this.

The SampleDataGroup has an Items property which is an ObservableCollection<SampleDataItem>.  Therefore, whenever new SampleDataItems are added or removed from the Items property, the PropertyChanged event will be fired by the collection on any user interface elements that bind to the Items collection.  Unfortunately, this is a bit anticlimactic because we do not see this happen in the template as it stands right now.  In other words, there’s no functionality that requires we remove items from the collection, and therefore we don’t see it in play.

The second important aspect I want to point out is that if we want observability at the SampleDataItem level — in other words, if we want user interface elements that bind to the Items collection to update when the properties of an item in that collection are changed, we would need to manually write the code to implement INotifyPropertyChanged in the SampleDataItem class.  Since that is not how the class is currently implemented, if you were to change the value of a property, such as the Title or Subtitle, while the app is running, you would NOT see the user interface update to reflect that change.  Why?  Because the SampleDataItem class doesn’t have the ability (again, as it is currently implemented) to fire the PropertyChanged event and notify any observers of the change.  However, I believe I’ll demonstrate this when we build an application later in this series that works with observable data.

Recap

In this lesson we talked about two very important ideas.  Hopefully you can see the larger role of MVVM and its influence on the Hub App Template.  That design pattern shaped and guided the developers of the template to provide an implementation that separated the concerns of the app and delegate responsibilities to certain classes.  Furthermore, hopefully you now understand, at least at a high level, why the Hub App Template is using  ObservableCollection<T>.  It provides a convenient means of updating user interface controls the notification to update itself when the underlying data source has changed.

There are more aspects of MVVM that we’ll cover in this series.  I’ll talk Commands later in this series which allows the user interface to bind events like a button click to a method in the ViewModel.

Embed

Download

Download this episode

The Discussion

Add Your 2 Cents