Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Resetting a View Model in WPF MVVM applications without code-behind in the view

0.00/5 (No votes)
17 Feb 2011 12  
The core idea behind this code and article is to provide an uncomplicated approach to reset a view-model without having to adjust DataContext references in views.

Introduction

The core idea behind this code and article is to provide an uncomplicated approach to reset a view-model without having to adjust DataContext references in views. It all started with an MSDN forum thread where someone asked if there was an easy way to clear a view model. He felt that clearing every property in the view model would be too cumbersome and that it would be simpler if he could have a Cancel button that would reset the DataContext to a new view model instance. The negative (so to speak) would be that he would have to have actual code in his view (the handler for the Cancel button) that did this. This problem intrigued me and I thought it would be an interesting exercise to design a set of classes that would make this a seamless process.

I kept two fundamental requirements in mind when I designed this class :

  1. The developer should be able to reset the view model without any code in his view.
  2. The class should be transparent, so the user should not have to change his existing view model in any way to use this class.

This code will only work with WPF, and I have tested this with .NET versions 4.0 (full and client profile) as well as 3.5 (full and client profile). The demo project is for Visual Studio 2010, but since it's .NET 3.5 compatible, you can easily use this from Visual Studio 2008 too. Unfortunately the code will not work on SilverLight as the classes it uses such as TypeDescriptionProvider are not available in SilverLight (even in version 4.0).

And lest anyone writes this off as yet another over-designed MVVM class targeting those who are anal retentive about avoiding code in their view, I think this class has value outside of it's code-in-the-view avoidance capabilities. *grin*

Why use this class at all?

Basarat Ali Syed wondered why I even needed to write this class. His exact words were:

Why not just use a ViewModel locator (which you are using) combined with a Messenger (also called mediator) to pass a message from the current View to the ViewModel locator to setup new properties.

This was basically what I replied to him:

While people can do that if they want to, not everyone likes that approach for these reasons:

  1. It's almost impossible to avoid code behind, since the view has to trigger a need to update the view model.
  2. You may have to have a View reference in the View Model (which breaks basic MVVM). Even if you use a Mediator, that's still just one level of indirection back to the view.
  3. Setting up new properties is a hassle, specially for complex view model classes

What my class achieves is this:

  1. You basically re-instantiate the view model. (this is analogous to having the magical ability to use the same this-pointer in C++ to point to a completely new instance, but at the same address)
  2. The view / original view model need to know nothing about what's going on (meaning the class is easy to plug in and out)
  3. No code behind in the view
  4. No need to add mediator/in-between classes for communication

And finally, it's important to remember that this is not the sort of class that you will just use everywhere and all the time. It has a specific design purpose and it does that role well. Feel free to ask more questions on this through the forum below.

Using the class with an existing MVVM application

Consider that you have an existing MVVM application where your MainWindow view is bound to a MainViewModel instance.

<Window x:Class="ResettableViewModelDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ResettableViewModelDemo"
        Title="Resettable View Model Demo Application" Height="350" Width="329" ResizeMode="NoResize"
        DataContext="{Binding Source={x:Static local:ViewModels.MainViewModel}}" Background="#FF485093">
    <Grid Margin="5" TextBlock.Foreground="White" TextBlock.FontSize="15">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left">
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="90" VerticalAlignment="Center">Item Code</TextBlock>
                <TextBox Text="{Binding ItemCode}" Width="197" Margin="8" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="90" VerticalAlignment="Center">Description</TextBlock>
                <TextBox Text="{Binding Description}" Width="196" Margin="8" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button Command="{Binding AddCommand}" Width="90" Margin="5">Add</Button>
                <Button Command="{Binding ExitCommand}" Width="90" Margin="5">Exit</Button>
            </StackPanel>
            <ListBox ItemsSource="{Binding Entries}" Height="180" Width="293" />
        </StackPanel>
    </Grid>
</Window>

Notice how the DataContext is set to a static property in the ViewModels class.

class ViewModels
{
    private static object mainViewModel = new MainViewModel();

    public static object MainViewModel
    {
        get { return ViewModels.mainViewModel; }
    }
}

MainViewModel will be your existing view model implementation.

class MainViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private void FirePropertyChanged(string propertyName)
  {
      PropertyChangedEventHandler handler = PropertyChanged;

      if (handler != null)
      {
          handler(this, new PropertyChangedEventArgs(propertyName));
      }
  }        

  private int itemCode = -1;

  public int ItemCode
  {
      get
      {
          return itemCode;
      }

      set
      {
          if (itemCode != value)
          {
              itemCode = value;
              FirePropertyChanged("ItemCode");
          }
      }
  }

  private string description = "unknown";

  public string Description
  {
      get
      {
          return description;
      }

      set
      {
          if (description != value)
          {
              description = value;
              FirePropertyChanged("Description");
          }
      }
  }

  private ICommand exitCommand;

  public ICommand ExitCommand
  {
      get
      {
          return exitCommand ?? (exitCommand = new DelegateCommand(() =>
          {
              Application.Current.MainWindow.Close();
          }));
      }
  }

  private ICommand addCommand;

  public ICommand AddCommand
  {
      get
      {
          return addCommand ?? (addCommand = new DelegateCommand(() =>
          {
              entries.Add(new { ItemCode = ItemCode, Description = Description });
          }));
      }
  }

  private ObservableCollection<object> entries = new ObservableCollection<object>();

  public ObservableCollection<object> Entries
  {
      get { return entries; }
  }

}

Now, assume that you have a new requirement to add a Cancel button to the view. Clicking the Cancel button must reset the two TextBoxes to the default values, and should also clear the ListBox. Without the ResettableViewModel class, you have two options - either reset all the bound properties through a Cancel command method, or add code-behind in your view to reset the DataContext to a brand new instance of the MainViewModel class. The former approach may be rather a lot of work when your view model is complex, and if you ever add/change a bound property or collection, you need to remember to reset that too. The latter approach is simple but a lot of MVVM aficionados would cringe at the thought of doing that. Oh, isn't this a conundrum? Enter the ResettableViewModel class!

Once you add the required source files to your project (or alternatively, add a reference to an assembly that contains these classes), you just need to do two little things. The first task is to change the static property to return a ResettableViewModel instance. (Note: if you set your DataContext in some other way, you'd have to make appropriate changes to do something similar)

class ViewModels
{
  private static object mainViewModel = 
      new NSViewModelExtensions.ResettableViewModel(new MainViewModel());

  public static object MainViewModel
  {
      get { return ViewModels.mainViewModel; }
  }
}

Now add a Cancel button to your XAML and bind its Command as follows.

<StackPanel Orientation="Horizontal">
  <Button Command="{Binding ResetCommand}" Width="90" Margin="5">Cancel</Button>
  <Button Command="{Binding AddCommand}" Width="90" Margin="5">Add</Button>
  <Button Command="{Binding ExitCommand}" Width="90" Margin="5">Exit</Button>
</StackPanel>

Notice how the Cancel button's command is bound to a ResetCommand. This is provided by the ResettableViewModel class, everything else will continue to come from your MainViewModel class. That's all, you are done! If you run the app now, clicking Cancel will reset your view model to its  initial state. Now, that wasn't a lot of work, was it?

Custom view model initialization

In some cases, your view model may not have a parameterless constructor, or you may need to set some initial properties. For those scenarios, the class provides a constructor that takes a Func<object> delegate that will be used to create the view model instance. Here's a simple example:

object mainViewModel = new NSViewModelExtensions.ResettableViewModel(
                () => new MainViewModel());

Here's a more typical example:

object mainViewModel = new NSViewModelExtensions.ResettableViewModel(
    () => 
        {
            return new MainViewModel()
            {
                ItemCode = 999,
                Description = "Default Item"
            };
        });

One caveat

If your view model has a ResetCommand property, it will will overridden by the ResetCommand property in the ResettableViewModel class. While it would probably be quite a rare situation, if you do run into it, then the workaround is to rename your property - sorry about that!

Implementation details

Put simply the ResettableViewModel class acts as a proxy to the actual view model instance. The only API newly added by the the ResettableViewModel class is the reset command, everything else comes from the underlying view model. It does this by implementing a custom TypeDescriptionProvider which dynamically provides properties on the ResettableViewModel instance that are fetched from the underlying view model object. I will quickly go through the various classes involved, and although the code is written to be quite self explanatory, I will add my comments where required. Here's what the ResettableViewModel class looks like.

[TypeDescriptionProvider(typeof(ResettableViewModelTypeDescriptionProvider))]
sealed class ResettableViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private void FirePropertyChanged(string propertyName)
  {
      PropertyChangedEventHandler handler = PropertyChanged;

      if (handler != null)
      {
          handler(this, new PropertyChangedEventArgs(propertyName));
      }
  }

  private static string ErrorViewModelTypeHasToMatch = 
    "The type of the new View Model has to match that of the old View Model.";

  private Func<object> creatorMethod;

  public ResettableViewModel(object innerViewModel, Func<object> creatorMethod = null)
  {
      this.InnerViewModel = innerViewModel;
      this.creatorMethod = creatorMethod;
  }

  public ResettableViewModel(Func<object> creatorMethod)
  {
      this.InnerViewModel = (this.creatorMethod = creatorMethod)();            
  }

  public ResettableViewModel(Type innerViewModelType)
  {
      this.InnerViewModel = Activator.CreateInstance(innerViewModelType);
  }

  internal object InnerViewModel { get; private set; }

  private ICommand resetCommand;

  public ICommand ResetCommand
  {
      get
      {
          return resetCommand ?? (resetCommand = new InternalDelegateCommand(() =>
              {                        
                  if (creatorMethod == null)
                  {
                      this.InnerViewModel = Activator.CreateInstance(
                          this.InnerViewModel.GetType());
                  }
                  else
                  {
                      var newViewModel = creatorMethod();
                      
                      if (this.InnerViewModel.GetType() != newViewModel.GetType())
                      {
                          throw new InvalidOperationException(
                              ResettableViewModel.ErrorViewModelTypeHasToMatch);
                      }

                      this.InnerViewModel = newViewModel;
                  }
                  
                  FirePropertyChanged(String.Empty);
              }));
      }
  }

  class InternalDelegateCommand : ICommand
  {
      private readonly Action executeMethod;

      public InternalDelegateCommand(Action executeMethod)
      {
          this.executeMethod = executeMethod;
      }

      public void Execute(object parameter)
      {
          if (this.executeMethod != null)
          {
              this.executeMethod();
          }
      }

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

      public event EventHandler CanExecuteChanged;
  }
}

Notice how the class has a TypeDescriptionProvider associated with it of type ResettableViewModelTypeDescriptionProvider (listed below). The reason I have a private inner class that implements ICommand is that I did not want to have any dependencies on any external command classes. This way, whatever MVVM framework you are on, the ResettableViewModel class would work smoothly since it has its own command class.

Pro-tip to update all bindings

Notice the call to FirePropertyChanged passing an empty string, well that will update every binding you have in your view.

The ResettableViewModelTypeDescriptionProvider class is simple and basically just provides a way for us to specify the custom type descriptor for our class.

class ResettableViewModelTypeDescriptionProvider : TypeDescriptionProvider
{
    private static TypeDescriptionProvider defaultTypeProvider = 
        TypeDescriptor.GetProvider(typeof(ResettableViewModel));

    public ResettableViewModelTypeDescriptionProvider()
        : base(defaultTypeProvider)
    {
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(
        Type objectType, object instance)
    {
        ICustomTypeDescriptor defaultDescriptor = 
            base.GetTypeDescriptor(objectType, instance);
        return instance == null ? defaultDescriptor : 
          new ResettableViewModelCustomTypeDescriptor(defaultDescriptor, instance);
    }
}

ResettableViewModelCustomTypeDescriptor is where we associate the properties in the inner class with our proxy class (the word proxy used very loosely there of course).

class ResettableViewModelCustomTypeDescriptor : CustomTypeDescriptor
{
  public ResettableViewModelCustomTypeDescriptor(ICustomTypeDescriptor parent, object instance)
      : base(parent)
  {
      customFields.AddRange(TypeDescriptor.GetProperties(
        ((ResettableViewModel)instance).InnerViewModel)
          .Cast<PropertyDescriptor>()
          .Select(pd => new ResettableViewModelCustomField(
                pd.Name, pd.PropertyType))
          .Select(cf => new ResettableViewModelCustomFieldPropertyDescriptor(
                cf)).Cast<PropertyDescriptor>());
  }

  private List<PropertyDescriptor> customFields = new List<PropertyDescriptor>();

  public override PropertyDescriptorCollection GetProperties()
  {
      return new PropertyDescriptorCollection(base.GetProperties()
              .Cast<PropertyDescriptor>()
              .Union(customFields)
              .ToArray());
  }

  public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
  {
      return new PropertyDescriptorCollection(base.GetProperties(attributes)
              .Cast<PropertyDescriptor>()
              .Union(customFields)
              .ToArray());
  }
}

ResettableViewModelCustomField is a simple class that represents a property. It's used to store the property name and property type for all the properties in the underlying view model instance.

class ResettableViewModelCustomField
{
  public ResettableViewModelCustomField(String name, Type dataType)
  {
      Name = name;
      DataType = dataType;
  }

  public String Name { get; private set; }

  public Type DataType { get; private set; }
}

ResettableViewModelCustomFieldPropertyDescriptor is where we actually fetch the property values from the underlying instance and return it to whoever asks for these properties on the outer class instance.

class ResettableViewModelCustomFieldPropertyDescriptor : PropertyDescriptor
{
  public ResettableViewModelCustomField CustomField { get; private set; }

  public ResettableViewModelCustomFieldPropertyDescriptor(
    ResettableViewModelCustomField customField)
      : base(customField.Name, new Attribute[0])
  {
      CustomField = customField;
  }

  public override bool CanResetValue(object component)
  {
      return true;
  }

  public override Type ComponentType
  {
      get 
      {
          return typeof(ResettableViewModel);
      }
  }

  public override object GetValue(object component)
  {
      return GetInnerPropertyInfo(component).GetValue(
        ((ResettableViewModel)component).InnerViewModel, new object[0]);
  }

  public override bool IsReadOnly
  {
      get 
      {
          return false;
      }
  }

  public override Type PropertyType
  {
      get
      {
          return CustomField.DataType;
      }
  }

  public override void ResetValue(object component)
  {
      this.SetValue(component, Activator.CreateInstance(CustomField.DataType));
  }

  public override void SetValue(object component, object value)
  {
      GetInnerPropertyInfo(component).SetValue(
        ((ResettableViewModel)component).InnerViewModel, value, new object[0]);
  }

  public override bool ShouldSerializeValue(object component)
  {
      return false;
  }

  private PropertyInfo propertyInfo;

  private PropertyInfo GetInnerPropertyInfo(object component)
  {
      return propertyInfo ?? (propertyInfo = ((ResettableViewModel)component)
        .InnerViewModel.GetType().GetProperty(this.CustomField.Name));
  }
}

The highlighted code in the above snippet is where we get and set properties on the underlying view model instance.

Conclusion

That's it, I guess. As usual, please feel free to give me feedback and criticism through the article forum. Your votes of 5 will be deeply appreciated too, and I will have a shot of single malt scotch for every 5 I get, and then maybe a few more!

History

  • February 16, 2011 - Article first published.
  • February 17, 2011 - Added text better explaining why this class was designed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here