ViewModelBase class

Base class for ViewModels by the MVVM pattern, with several simplifications for derived classes.

Probably every WPF project following the MVVM pattern (Model–View–ViewModel) has a class called “ViewModelBase” somewhere. It serves as base class for all ViewModel classes of the application and provides an implementation of the INotifyPropertyChanged interface in most cases. This is at least, besides the event, a protected method like “OnPropertyChanged” or “RaisePropertyChanged”.

But that alone doesn’t make the programmer’s life easier yet when there are numerous classes with equally numerous properties to be implemented, with dependencies among them (computed properties).

My implementation of this class provides the following simplifications for WPF applications:

  • DisplayName property
    Shared DisplayName property for all ViewModel classes. It can be used to display an object in lists, for instance. For windows, it can also be used as the window title.
  • Infrastructure for commands
    The virtual InitializeCommands method is called by the constructor and allows derived classes to keep their command-related code together in one place.
  • GetValue, SetValue
    GetValue and SetValue methods that avoid the need for backing fields in the classes. They are similar to DependencyProperties in a way.
  • Sanitising text input
    Methods for cleaning up text input for numbers, dates etc. ViewModel classes must often convert a special data type (int, DateTime…) to a string for user input. As it can be freely typed into a TextBox by the user, it should be cleaned and reformatted on updating.
  • Validation
    Validating of properties for marking input errors and displaying error messages. With support for lists of sub-objects that can be edited and provide errors but are displayed in the same view.
  • INotifyPropertyChanged implementation
    OnPropertyChanged method with support for CallerMemberName (from C# 5 on), multiple property names at once, expressions and checking of the specified property names for existence in the class (debug build only).
  • Dependent properties (reverse notification)
    Declarative definition of dependent properties for which change notifications must be raised when the used property changes. The dependent property is marked with the NotifiesOn attribute, instead of letting the changing property notify for all properties that might use its value.
  • Dependent commands (reverse notification)
    Declarative definition of dependent commands whose availability (CanExecute) changes when the used property changes. The dependent command is marked with the NotifiesOn attribute, instead of letting the changing property update all commands that might use its value.
  • IsModified management
    Declarative definition of those properties that set the provided IsModified property to true on changes.
  • ViewState
    Provides a flexible store for the current view state when navigating back and forth, similar to ViewBag from ASP.NET MVC.
  • Change handler methods
    Declarative attribution of change methods that shall be called when a property changes. The handler method is marked with the PropertyChangedHandler attribute that specifies which property to connect with.
  • Special derived classes
    Definition of special ViewModel classes for simple uses: EmptyViewModel, ValueViewModel<T>

Some ideas are taken from Steve Cadwallader who provides a thorough explanation in three articles (1, 2, 3).

Compatibility: .NET Version 4.0 or newer

You can have these features at even less writing, full performance and less memory consumption with the newer Fody add-in ViewModelKit.Fody.

Example

The following sample class shows a derived class of ViewModelBase:

public class PersonViewModel : ViewModelBase
{
    #region Data properties

    // Replacing the base DisplayName property
    public override string DisplayName
    {
        get { return FirstName + " " + LastName; }
        set { }
    }
   
    // Usage of GetValue and SetValue with C# 4 (.NET 4.0, Visual Studio 2010)
    public string FirstName
    {
        get { return GetValue<string>("FirstName"); }
        set { SetValue(value, "FirstName"); }
    }

    // Usage of GetValue and SetValue with C# 5 (.NET 4.5, Visual Studio 2012+)
    public string LastName
    {
        get { return GetValue<string>(); }
        set { SetValue(value); }
    }
   
    // The change method is called when LastName was changed (after the value is stored
    // and before the PropertyChanged event is raised)
    [PropertyChangedHandler("LastName")]
    private void OnLastNameChanged()
    {
        if (knownNames.Contains(LastName))
        {
            SayHelloCommand.TryExecute();
        }
    }
   
    // Reformatting of a date from text input;
    // Changing this property sets IsModified to true
    [SetsModified]
    public string Birthdate
    {
        get { return GetValue<string>(); }
        set { SetValue(SanitizeDate(value)); }
    }
   
    // Changes with Birthdate
    [NotifiesOn("Birthdate")]
    public int Age
    {
        get
        {
            return (DateTime.Now - DateTime.Parse(Birthdate)).Years;   // Pseudocode!
        }
    }

    #endregion Data properties
   
    #region Commands

    // Declaration of all commands;
    // SayHello depends on the current Birthdate
    [NotifiesOn("Birthdate")]
    public DelegateCommand SayHelloCommand { get; private set; }

    // Initialisation of the commands, is called from the constructor
    protected override void InitializeCommands()
    {
        SayHelloCommand = new DelegateCommand(OnSayHello, CanSayHello);
    }
   
    // Command handler methods
    private bool CanSayHello()
    {
        return DateTime.Parse(Birthdate) < DateTime.Now;
    }
   
    private void OnSayHello()
    {
        Console.WriteLine("Hello " + DisplayName + "!");
    }

    #endregion Commands
}

Download

ViewModelBase.cs33.7 KiBSource code of the ViewModelBase class

Usage notes

You need the CollectionDictionary class and the DelegateCommand class to use the ViewModelBase class.

Depending on whether you are using the ViewModelBase class in a project targeting .NET 4.0 or from 4.5, you must enable or disable the CSHARP50 define at the beginning on the file. This is used to write compact code using the CallerMemberName attribute for both environments.

Changes

2015Aug27
  • IsModified management with a predefined property, set to true whenever a property marked with the SetsModified attribute is changed. Reset this in your code after loading and saving. The DisplayName property can be included by setting the DisplayNameSetsModified property.
  • ViewState management with a dynamic object, much like ViewBag in ASP.NET MVC. Lets you store and restore the current state of a view when navigating back and forth.
  • Dependent commands: Commands marked with the NotifiesOn attribute raise their CanExecuteChanged event when the specified property changes. (Requires the DelegateCommand and CollectionDictionary classes.)
  • Fix: Removed static reflection cache that may have caused memory leaks by keeping a reference to old ViewModel instances.
  • Code cleanup (formatting).

Licence and terms of use

Copying and distribution of this file, with or without modification, are permitted provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. (GNU All-Permissive licence)

Statistic data

  • Created on 2012-07-09, updated on 2015-08-27.
  • Ca. 650 lines of code, estimated development costs: 650 - 2 600 €