Saturday, September 15, 2012

Static Properties and Change Notification [WPF 4.5 RC New Features]

Change propagation is one of the features in XAML technologies which makes MVVM possible. We have included it as one of the MVVM enablers for WPF and Silverlight in the book MVVM Survival Guide for Enterprise Architectures in WPF and Silverlight. Up until .net 4.0, change propagation support was only available for instance properties i.e. they weren't available for static properties. In .net 4.5, this feature is added. In this post, we are going to discuss how we can add change propagation support for our static properties.

Let's add a user control which takes first and last names as input. It binds the Text property of these text boxes to properties in the data context.

Now let's add another user control, it also expects properties with the same names in the data context. The only difference is that it is a view only kind of a control which just displays the values of these properties in the form of a TextBlock. Since there is no change propagation from view to the data context side, we are using OneWay mode for Binding.

Now we can add these controls to a Window. This window is using a DockPanel to display these controls docking the edit control on top and the view-only control on the bottom side. It is also adding a TextBlock to show banner. In case you haven't noticed, this is a Cool Student App. The view is also instantiating MainViewModel in-line and using it as the DataContext. The is passing the Student property from the DataCotext as the DataContext of both the user controls.

Now let's look at the definition of MainViewModel. As expected, it has a property, named Student.

Now let's see how we can add Student type. As expected by the two user controls, it has FirstName and LastName properties. The view model is implementing INotifyPropertyChanged to support change notifications.

Now let's run this cool application. The following view is shown on the screen. Change the FirstName and LastName. Notice that the same values appear in the view-only user control as well. This is all because of change propagation support in WPF through INotifyPropertyChanged implementation by the view model.

Let's add the following to StudentViewModel.cs. Here we want to maintain the number of students created into the Student type itself. We do this by introducing _numberOfStudents. In order to have ease in binidng, we are exposing a string based property NumberOfStudents which just returns the value of _numberOfStudents. We also have introduced a changed event for the property. We will be discussing this event later in this discussion.

Just for the example purposes, let's assume we have a timer which fakes the creation of students. This is done by just incrementing the _numberOfStudents counter. Since OnPropertyChanged() is an instance method, we cannot directly use it in the property setter for the counter property.

If we just run it like this there would be no updates in the view for the changes in the static property. This shows that PropertyChanged() doesn't support changed notification for static properties. You can verify this by using this code after updating the view as specified later in this discussion. This would show the view without any change notifications for the static property.

[NameOfProperty]Changed Event for Static Properties
And that is why we introduced NumberOfStudentsChanged. This would allow us to provide change notifications for the changes in the static property. Let's update the code like this. This is a new feature of .net 4.5. There are no worth-mentioning arguments for this event. The first argument is passed as null and the other as Empty. This is unlike PropertyChanged event where we need to pass Magic string. The magic string must be exactly same as the name of the property, otherwise, the property would have no change notification support. Here the name of event is enough. It has to be named as [NameOfProperty]Changed.

Now let's try showing the number of students in our view.

x:Static Markup Extension
In order to show the static members, the first choice which comes to our mind is the use of x:Static markup extension. In the snippet below we are using a TextBlock for this purpse. Here we are using x:Static extension to display the same.

Let's run the application now. It shows up as follows:

It's quite strange as the number of students count is not being updated. From the screenshot below, you can see that this is inspite of the value being updated.

Basically this is because x:Static doesn't support change propagation no matter how source property is defined. But now you should atleast realize why we introduced NumberOfStudents as string type. Basically the markup extension doesn't do the necessary value conversions compared to Binding markup extension. This is what msdn has to say about this:

Use caution when you make x:Static references that are not directly the type of a property's value. In the XAML processing sequence, provided values from a markup extension do not invoke additional value conversion.

Using Binding Markup Extension with Parenthesis in Property Path [Partial Qualification]
As we have seen above, x:Static supports displaying values of static properties but it doesn't support change notification. As we know Biniding resource extension supports specification of source using the simple DataContext or by specifying ElementName or Static / Dynamic resource extensions. But we should be having instance of the source object to bind to its properties. Does it support binding to a type's static members. Actually, it does but the syntax is a bit different. This is the same syntax used to bind to attached properties. It is to say that directly bind to whatever is specified in the Path property without worrying about the source. This can be called the Parenthesis syntax for binding. You can find further details about this on msdn. Here the source property can be an attached property or a static property.

Let's update the TextBlock used to display the NumberOfStudents from StudentViewModel as follows:

The designer gets a little confused as the source property is bound like an attached property but in actual it is not defined like one. You see the following issue in the designer window.

But the code does build fine and when you run the application you see the view as displayed below:

You can see the count being updated. This means this does support change notification. Here the change notification is supported by the NumberOfStudentsChanged event which is used by the Binding extension.

event EventHandler<PropertyChangedEventArgs>
Now our view models can be more complex. The might have more than a few properties defined. Adding an event for each of the property would be a real task. Yes, we have an alternative.

This is the second option provided in .net 4.5 for achieving the same result. This is more like our implementation of INotifyPropertyChanged where we added an OnPropertyChanged method. Why can't we add an interface like that for our static properties. The answer is, Because we can't... The interfaces are defined for instances to provide the details about the object's features i.e. what an object can do. Most certainly, we can define the method in one of the base classes used for view models. Now the child classes can use this for their static methods if it is marked as protected. No, since it doesn't make sense, a static member cannot be marked as virtual or abstract :)

Now we can use this in the same place in the constructor as follows:

Is Partial Qualification necessary for Binding
We don't need to use partial qualification. We can also bind these properties using the DataContext as follows:

Here the source property is used from the current DataContext.
Download Code:

No comments: