Silverlight / WPF, MVVM and Dependencies

As promised I will write some technical Windows Phone related things. I will start off with a MVVM specific framework I have developed during some projects.

I like MVVM. The whole Idea of separating the view logic from the view is awesome and really fits into the Silverlight / WPF (I will refer to them as XAML from now on) world. As with every technology there are ups and downs and so has XAML its own downs. Two of them I want to explain here and show solutions for them.

String parameter passing for PropertyChanged events.

This problem will be gone with the next .NET version. The problem is that the compiler doesn’t check string contents :). Lets assume you have the following class:

public class ContactViewModelTraditional : INotifyPropertyChanged
{
	private string _Name;
	public string Name
	{
		get
		{
			return _Name;
		}
		set
		{
			bool changed = value != _Name;
			_Name = value;
			if (changed)
				OnPropertyChanged("Name");
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	private void OnPropertyChanged(string propertyName)
	{
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}
}

If you now want to change the property name „Name“ to something else you have to think of the string parameter for the OnPropertyChanged method. If you forget to rename this string the compiler will not complain. You can see your error only at runtime (if you are lucky).

To solve this issue Microsoft has introduced the [CallerMemberName] attribute to make the parameter generated automatically.
For now, as we don’t have such a feature, I have chosen to (mis)use Lambda expressions to pass the property name. This way costs performance but ensures that the property name is correct or the compiler will detect it.

The class from above would look like this using my approach (I will explain the function call to „SetPropertyValue“ later):

public class ContactViewModelTraditional : INotifyPropertyChanged
{
	private string _Name;

	public string Name
	{
		get
		{
			return _Name;
		}
		set
		{
			this.SetPropertyValue(p => p.Name, ref _Name, value);
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	private void OnPropertyChanged(string propertyName)
	{
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}
}

The key in this example is the setter of the Name property. Here you can see that the name of the property gets passed as a Lambda expression. The „SetPropertyValue“ method then has to get the name from the lambda expression and pass it to the „OnPropertyChanged“ method.

Property dependencies and raising the correct PropertyChanged events

Another big problem arises when the ViewModel structure gets more complex and / or if properties are based on other properties and only change implicitly.

So lets assume we have a simple ViewModel and this ViewModel contains a ContactViewModel. We want a property „CurrentContactNameString“ on the ViewModel that returns the Name of the current contact or the string „No Contact selected“ if there is no current contact.

The ViewModel would look like this:

public class MainViewModelTraditional : INotifyPropertyChanged
{
	private ContactViewModelTraditional _CurrentContact;
	public ContactViewModelTraditional CurrentContact
	{
		get
		{
			return _CurrentContact;
		}
		set
		{
			bool changed = _CurrentContact != value;
			if (changed)
			{
				if (_CurrentContact != null)
					_CurrentContact.PropertyChanged -= CurrentContact_PropertyChanged;
				_CurrentContact = value;
				if (_CurrentContact != null)
					_CurrentContact.PropertyChanged += CurrentContact_PropertyChanged;
				OnPropertyChanged("CurrentContact");
				OnPropertyChanged("ContactNameString");
			}
		}
	}

	void CurrentContact_PropertyChanged(object sender, PropertyChangedEventArgs e)
	{
		if (e.PropertyName == "Name")
			OnPropertyChanged("ContactNameString");
	}

	public string CurrentContactNameString
	{
		get
		{
			if (CurrentContact != null)
				return CurrentContact.Name;
			return "No Contact selected";
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	private void OnPropertyChanged(string propertyName)
	{
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}
}

You can see many „problems“ in this little example:

  1. Event passing between INotifyPropertyChanged instances
    You can clearly see in the setter of the CurrentContact property what has to be done if you rely on data inside a „foreign“ INotifyPropertyChanged instance.
    You have to make sure to listen to the PropertyChanged event of the instance to set and remove the event handler from the instance that leaves.
    If you e.g. forget to remove the event handler from the „old“ instance then you will get too many change-events. It’s very hard to detect something like this.
  2. The property logic is spread all over the class
    To fire the correct PropertyChanged events the source of the event has to know exactly what properties rely on it.
    In this example the setter for „CurrentContact“ and the event handler for the „Name“ property of the current contact have to know that the property „CurrentContactNameString“ uses them. In this example the dependencies are relatively simple but if this gets more complex you easily loose the overview.
    Every change on the target implies a reorganization of all the PropertyChanged sources to match the used logic again.

Results only change if the data used to get it changes

For those two problems I have developed a dependency tracking system. The central idea is that every result only changes whenever any data changes that has been used to get the result.
Sounds simple, right? It is that simple!

So I have developed a tracking mechanism that tracks all data that gets used to get a result (finally for a property getter). Whenever data changes all PropertyChanged events for results that depend on this data get fired.

The ViewModel sample using this approach looks like this:

public class MainViewModel : IFireNotifyPropertyChanged
{
	private ContactViewModel _CurrentContact;
	public ContactViewModel CurrentContact
	{
		get
		{
			using (this.Track(p => p.CurrentContact))
				return _CurrentContact;
		}
		set
		{
			this.SetPropertyValue(p => p.CurrentContact, ref _CurrentContact, value);
		}
	}

	public string CurrentContactNameString
	{
		get
		{
			using (this.Track(p => p.CurrentContactNameString))
			{
				if (CurrentContact != null)
					return CurrentContact.Name;
				return "No Contact selected";
			}
		}
	}

	public void FireNotifyPropertyChanged(string propertyName)
	{
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}

	public event PropertyChangedEventHandler PropertyChanged;
}

As you can see the property „CurrentContactNameString“ only has to use the „CurrentContact“ and the „Name“ property. Whenever one of them changes the PropertyChanged event for „CurrentContactNameString“ gets fired.

The integration of the framework is relatively simple:

  1. every getter (that is relevant) uses a Tracker instance as long as relevant data gets accessed. The creation of this Tracker instance is hidden by a extension method „Track“ for INotifyPropertyChanged.
  2. setter have to use the SetPropertyValue extension that triggers all the logic to notify dependent properties about this change
  3. (Only Silverlight for now) Instead of INotifyPropertyChanged the classes have to implement IFireNotifyPropertyChanged which extends INotifyPropertyChanged by a method to trigger a PropertyChanged event. This is necessary because Silverlight doesn’t allow to access private fields through reflection (and this is the only way to trigger an event from outside an instance)

The downside of this framework is that it can get slow very fast. Because every change triggers the PropertyChanged event for all dependent properties the amount of triggered events can explode easily. So use it with care 🙂
The real benefit is that you don’t have to care about dependencies between Properties or INotifyPropertyChanged instances and – even more important – don’t have to think about them during a refactoring!

I will explain the implementation of this framework in the next post after I have reorganized and published it to some Open Source hoster. So stay tuned 😉

Devmil

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.