Handling interactions between ViewModel classes

I’ve been puzzling over what seemed to be an insurmountable problem lately. It seemed that the guys who came up with the MVVM design pattern totally missed the boat on anything more than a simple, single-page app. It couldn’t be me, since I’m a SuperGenius ™.

Well, OK, it did turn out to be me. Once I stopped flailing about in WPF-land and actually thought about the problem, it became easy.

The Problem

What I was trying to do seemed pretty simple, and I couldn’t figure out how to make it work, which is why I was sure it was them and not me – how wrong I would be…

Basically, I had an app with multiple views. In one view, I was doing something that caused some data to be updated. I wanted to show the updated data in another view. Since the two views were using two different ViewModels, I couldn’t figure out how data binding by itself could solve the problem. Then it came to me – the two different ViewModels were sharing the idea of the data they were both showing, and data is supposed to live in the Model layer. Duh!

Once this epiphany drilled its way through my head, I figured out how to solve the problem (Note – I’m 100% new to WPF, so there is every chance there is an easier way to do this. If you know it, please let me know!)

The Solution

The key to this was to make both my ViewModels implement INotifyPropertyChanged to hook them up to propagate changes to and from their Views. Then I created an event in the Model that would be raised whenever the underlying data they were sharing was changed. My model looked like this:

   1:     public class Model
   2:      {
   3:          private String modelValue;
   5:          public delegate void ModelChangedEvent(object sender, ModelChangedEventArgs e);
   6:          public event ModelChangedEvent ModelChanged;
   7:          public void SetValue(string newValue)
   8:          {
   9:              modelValue = newValue;
  10:              if(ModelChanged != null)
  11:              {
  12:                  ModelChanged(this, new ModelChangedEventArgs {OldValue = modelValue, NewValue = newValue});
  13:              }
  14:          }
  15:      }

And I had my target ViewModel listen for this event. In the event handler for the ModelChangedEvent, the ViewModel used the newValue to set the value it needed to show on the View, which caused the PropertyChanged event to be raised, and everything worked like a champ. Here is the target ViewModel:

   1:      public class ReflectedViewModel : INotifyPropertyChanged
   2:      {
   3:          private string reflectedValue;
   4:          private Model model;
   5:          public event PropertyChangedEventHandler PropertyChanged;
   7:          public Model Model
   8:          {
   9:              get { return model; }
  10:              set
  11:              {
  12:                  if(model != null) throw new InvalidOperationException("Attempted to set model more than once.");
  13:                  model = value;
  14:                  model.ModelChanged += model_ModelChanged;
  15:              }
  16:          }
  18:          void model_ModelChanged(object sender, ModelChangedEventArgs e)
  19:          {
  20:              ReflectedValue = e.NewValue;
  21:          }
  23:          public String ReflectedValue
  24:          {
  25:              get { return reflectedValue; }
  26:              set
  27:              {
  28:                  if(value.Equals(reflectedValue) == false)
  29:                  {
  30:                      reflectedValue = value;
  31:                      if(PropertyChanged != null)
  32:                      {
  33:                          PropertyChanged(this, new PropertyChangedEventArgs("ReflectedValue"));
  34:                      }
  35:                  }
  36:              }
  37:          }
  38:      }

You can download the entire example from here.


This was not a hard problem to solve, once I stopped and actually thought about it. I got so wrapped up in the new framework and toys (WPF/XAML) that I forgot about everything else I knew for a bit 🙂

As usual, any and all comments appreciated. Comments telling me about easier and more idiomatically correct ways of writing this are 100% welcomed!

— bab