The INotifyPropertyChanged interface, while easy to implement, is hard to consume. Registering for notification events requires extra bookkeeping code that obfuscates business logic. Update Controls is an open source library that replaces event-based data binding with automatic discovery and updating. Powerful yet difficult patterns

become simple. WPF data binding requires that a bindable class implement the INotifyPropertyChanged interface. This interface is fairly easy to implement as it requires only a single event: PropertyChanged. This event is to be fired every time one of the properties is changed.

The problem with this interface is in the code that consumes it. The consumer of the interface usually needs to update a property. The value of that property depends upon properties of other objects, and so the dependent object registers for the PropertyChanged event. When this event is fired, the dependent object calculates the value of the dependent property, stores it, and fires another PropertyChanged event upstream. Listing 1 shows an example.

Much of the code using INotifyPropertyChanged is concerned with registering for the PropertyChanged event, reregistering when the subject changes, and firing it at the appropriate time. The actual business logic is lost in a sea of bookkeeping.

Much of the code using INotifyPropertyChanged is concerned with registering for the PropertyChanged event, reregistering when the subject changes, and firing it at the appropriate time. The actual business logic is lost in a sea of bookkeeping.

What would this code look like if you could take INotifyPropertyChanged out of the picture? What if data binding could work directly against a property and update whenever it changed? In that ideal world, the bookkeeping code goes away and you are left with just the business logic, as shown in Listing 2.

This ideal code really works-if you use Update Controls. Update Controls is an open source software library that does not require you to implement the INotifyPropertyChanged interface or fire PropertyChanged events. You just need to write your business logic. The Update Controls library figures out when your properties change and automatically updates the view. You can download the library from Codeplex (http://www.codeplex.com/updatecontrols) or UpdateControls.NET (http://updatecontrols.net/).

The Update Controls library figures out when your properties change and automatically updates the view.

The Update Controls library

To use Update Controls, add two references to your C# or Visual Basic WPF project: UpdateControls.dll and UpdateControls.XAML.dll. Within your XAML files, add a namespace reference to UpdateControls.XAML.

<Window ...
    xmlns:u="clr-namespace:UpdateControls.XAML;
assembly=UpdateControls.XAML"
    ... >

Now use “{u:Update}” everywhere that you would otherwise use “{Binding}”. Your WPF view will be updated whenever the property changes. For example:

<TextBlock Text="{u:Update MailMerge}"/>

MailMerge is an example of a dependent property because it depends upon other properties for its value. CurrentCustomer and Name, on the other hand, are independent properties since they can change independently. Independent properties support both read and write, but dependent properties are read-only. As you’ve seen, writing a dependent property is simple; just put the business logic into the get method. But writing an independent property takes one additional step. You need to create an Independent sentry object that keeps track of every get and set. Listing 3 shows the CurrentCustomer property as an example.

The Independent sentry tells Update Controls that MailMerge’s getter has called CurrentCustomer’s getter. It also makes MailMerge out-of-date when CurrentCustomer’s setter is invoked. Creating these sentries is all you need to do, but you need to do it for every independent property.

To help you write this extra code, Update Controls installs a Visual Studio add-in that shows up in the Tools menu as “Generate Independent Properties”. It is mapped, by default, to Ctrl+D, G (as well as Ctrl+D, Ctrl+G, just in case). Select any private field or set of private fields in a C# or Visual Basic class, hit Ctrl+D, G, and the add-in will generate the Independent sentry and property.

Whereas INotifyPropertyChanged forces you to write bookkeeping code for intermediate logic, Update Controls makes it trivial. Because of this, Update Controls simplifies patterns that are much more cumbersome using INotifyPropertyChanged.

The Presentation Model Pattern

In 2004, Martin Fowler documented the Presentation Model pattern (http://martinfowler.com/eaaDev/PresentationModel.html). In 2006, John Gossman of the Expression team gave the name Model-View-ViewModel to a realization of this pattern in WPF (http://blogs.msdn.com/johngossman/archive/2006/02/27/540304.aspx). In either case, the goal is separation of concerns.

The pattern separates the data model from the view by interjecting an intermediate model created specifically for presentation. This intermediate presentation model transforms the data model into a form that the view can easily consume. The presentation model is an adapter between the data model and the view. See Figure 1 for a diagram of the resulting structure.

Figure 1: Structure diagram of the Presentation Model pattern. The view has direct access to the presentation model, and indirect access to the data mode.
Figure 1: Structure diagram of the Presentation Model pattern. The view has direct access to the presentation model, and indirect access to the data mode.

Why is this important? The data model is concerned with storing data. It should be a faithful representation of the problem domain. It should be useful not only to the UI, but also to business logic. It should not have any presentation logic-you should delegate presentation logic to the presentation model.

The CRMPresentation class in Listing 2 is an example of a presentation model. It is an adapter between the Customer data model and the view. But the view also needs direct access to the data model for its other behavior. Since a WPF component can have only one DataContext, you’ll add a pass-through property to the presentation model.

public CustomerSelector CustomerSelector
{
    get { return _customerSelector; }
}

Or in VB:

Public ReadOnly Property CustomerSelector() _
    As CustomerSelector
    Get
        Return _customerSelector
    End Get
End Property

Now the view has access to the data model. The view binds to the current Customer for Name and to the presentation model for MailMerge. It uses the pass-through property to get from the presentation model back to the data model, like this:

<StackPanel>
    <TextBox
        Text="{u:Update
        CustomerSelector.CurrentCustomer.Name}"/>
    <TextBlock
        Text="{u:Update MailMerge}"/>
</StackPanel>

The Navigation Model pattern

WPF makes it really easy to bind one control to another. You might bind the scaling transform of an image to a zoom slider, for example. Or you might bind the selection in a list to the data context of a form so that you can edit the selected item.

Binding controls together in this way tightly couples them. It makes it difficult to decompose the UI into separate user controls for easier maintenance. And it makes it hard to make two windows work together when they are not part of the same visual tree.

The solution is to move this UI selection state out of the controls and into a shared object-what I call a navigation model. All of the controls can then bind to the navigation model rather than to each other. This model doesn’t store application data; that’s the job of the data model. The navigation model stores the user’s point-of-view from which they observe the data model. As the user selects items in a list, they are navigating through application data. A structure diagram appears in Figure 2.

Figure 2: Structure diagram of the Navigation Model pattern. The navigation model holds transient state, while the data model holds persistent state.
Figure 2: Structure diagram of the Navigation Model pattern. The navigation model holds transient state, while the data model holds persistent state.

The biggest difference between the navigation model and the data model is that the data model is persistent whereas the navigation model is transient. Application data is stored in a database and shared among many users. Navigation data exists only in memory. It sticks around only as long as the user’s session, and goes away when they close the window.

The CustomerSelector class in Listing 3 is a navigation model. It stores the currently selected customer. Three controls bind to the CustomerSelector: customer name, mail merge text, and customer list. The user controls the selected customer with the list and the other two controls pick it up from there.

<ListBox
    ItemsSource="{u:Update CRMData.Customers}"
    SelectedItem="{u:Update
        CustomerSelector.CurrentCustomer}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <ListBoxItem
                Content="{u:Update Name}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

By sharing a navigation model, all controls can be linked without referring to each other.

One final benefit of the navigation model pattern is that it becomes easy to programmatically navigate on the user’s behalf. To select a new customer when they are added to the CRM system, just change the CurrentCustomer property in the navigation model. The list, the name control, and the mail merge all respond to this one change.

public void NewCustomer()
{
    _customerSelector.CurrentCustomer =
        _crmData.NewCustomer();
}

Or in VB:

Public Sub NewCustomer()
    _customerSelector.CurrentCustomer = _
        _crmData.NewCustomer()
End Sub

Advantages of Asynchronous Updates

In these examples you’ve seen how Update Controls takes care of notification on your behalf, eliminating a significant amount of bookkeeping code. You never have to write event registration code again. It simplifies patterns that are otherwise difficult to implement.

But there are advantages that you don’t see. PropertyChanged events are fired synchronously. A PropertyChanged event is going to be handled immediately; consumers aren’t going to wait until all changes are complete. If one user action causes multiple properties to change, and one consumer is dependent upon all changes, multiple PropertyChanged events will be fired. This causes a cascade of redundant updates.

Have you ever seen a list box with so many items that the scroll bar is a thin horizontal line? If you select all of the items and press the delete button, you might see an hourglass appear. The thin line of the scrollbar will grow, slowly at first, then faster, until it fills the height. Finally, the list is empty and the hourglass disappears.

What’s happened is that a loop somewhere in the code is calling a method to delete each item. That method fires an event to the list box informing it that the list has changed. The list box removes the item, repaints itself, and updates the scroll bar. Only then does the method return to the loop so it can be called again.

Update Controls, on the other hand, works asynchronously. It waits until all changes are completed before updating the view. Even if the view depends upon all of those changes, it is updated only once. The scroll bar of a list box using u:Update is not repainted until all of the items are deleted.

Another advantage of asynchronous updates is thread safety. A synchronous event is handled on the thread on which it is fired. Since the UI is the consumer of PropertyChanged events, all PropertyChanged events must be fired on the UI thread. If a background thread changes properties visible to the UI, you must take special care to move that event across threads.

Since Update Controls works asynchronously, changes can occur on any thread. You must, of course, use locks to protect your data from simultaneous access, but there is no additional code needed to make updates thread safe. Changes made by a background thread automatically cause the foreground thread to update the UI.

Download the library to see all of these advantages for yourself. Once you start using Update Controls on a regular basis, I think you’ll agree that INotifyPropertyChanged is obsolete.

Listing 1 (C#): Mail merge using INotifyPropertyChanged

public class CRMPresentation : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private CustomerSelector _customerSelector;
    private Customer _currentCustomer;
    private string _mailMerge;

    public CRMPresentation(CustomerSelector customerSelector)
    {
        _customerSelector = customerSelector;
        UpdateCurrentCustomer();

        // Register for changes to the selected customer.
        _customerSelector.PropertyChanged +=
            new PropertyChangedEventHandler(
                CustomerSelector_PropertyChanged);
    }

    public string MailMerge
    {
        get { return _mailMerge; }
    }

    private void CustomerSelector_PropertyChanged(
        object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "CurrentCustomer")
        {
            UpdateCurrentCustomer();
        }
    }

    void CurrentCustomer_PropertyChanged(
        object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Name")
        {
            UpdateMailMerge();
        }
    }

    private void UpdateCurrentCustomer()
    {
        // Unregister from the old customer.
        if (_currentCustomer != null)
            _currentCustomer.PropertyChanged -=
                new PropertyChangedEventHandler(
                    CurrentCustomer_PropertyChanged);

        // Make the switch.
        _currentCustomer = _customerSelector.CurrentCustomer;
        UpdateMailMerge();

        // Register with the new customer.
        if (_currentCustomer != null)
            _currentCustomer.PropertyChanged +=
                new PropertyChangedEventHandler(
                    CurrentCustomer_PropertyChanged);
    }

    private void UpdateMailMerge()
    {
        // Calculate the new mail merge.
        Customer recipient = _currentCustomer;
        if (recipient == null)
        {
            _mailMerge = string.Empty;
        }
        else
        {
            string name = recipient.Name;

            _mailMerge = string.Format(
                "Congratulations {0}, you are a winner!",
                name);
        }

        // Notify others that the mail merge has changed.
        FirePropertyChanged("MailMerge");
    }

    private void FirePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged.Invoke(this,
                new PropertyChangedEventArgs(name));
    }
}

Listing 1 (VB): Mail merge using INotifyPropertyChanged

Public Class CRMPresentation
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged

    Private _customerSelector As CustomerSelector
    Private _currentCustomer As Customer
    Private _mailMerge As String

    Public Sub New(ByVal customerSelector As CustomerSelector)
        _customerSelector = customerSelector
        UpdateCurrentCustomer()

        ' Register for changes to the selected customer.
        AddHandler _customerSelector.PropertyChanged, _
            AddressOf Me.CustomerSelector_PropertyChanged
    End Sub

    Public ReadOnly Property MailMerge() As String
        Get
            Return _mailMerge
        End Get
    End Property

    Private Sub CustomerSelector_PropertyChanged( _
        ByVal sender As Object, _
        ByVal e As PropertyChangedEventArgs)
        If e.PropertyName = "CurrentCustomer" Then
            UpdateCurrentCustomer()
        End If
    End Sub

    Private Sub CurrentCustomer_PropertyChanged( _
        ByVal sender As Object, _
        ByVal e As PropertyChangedEventArgs)
        If e.PropertyName = "Name" Then
            UpdateMailMerge()
        End If
    End Sub

    Private Sub UpdateCurrentCustomer()
        ' Unregister from the old customer.
        If _currentCustomer IsNot Nothing Then
            RemoveHandler _currentCustomer.PropertyChanged, _
                AddressOf CurrentCustomer_PropertyChanged
        End If

        ' Make the switch.
        _currentCustomer = _customerSelector.CurrentCustomer
        UpdateMailMerge()

        ' Register with the new customer.
        If _currentCustomer IsNot Nothing Then
            AddHandler _currentCustomer.PropertyChanged, _
                AddressOf CurrentCustomer_PropertyChanged
        End If
    End Sub

    Private Sub UpdateMailMerge()
        ' Calculate the new mail merge.
        Dim recipient As Customer
        recipient = _currentCustomer
        If recipient Is Nothing Then
            _mailMerge = String.Empty
        Else
            _mailMerge = String.Format( _
                "Congratulations {0}, you are a winner!", _
                recipient.Name)
        End If

        ' Notify others that mail merge has changed.
        FirePropertyChanged("MailMerge")
    End Sub

    Private Sub FirePropertyChanged(ByVal name As String)
        RaiseEvent PropertyChanged(Me, _
            New PropertyChangedEventArgs(name))
    End Sub
End Class

Listing 2 (C#): Mail merge using Update Controls

public class CRMPresentation
{
    private CustomerSelector _customerSelector;

    public CRMPresentation(CustomerSelector customerSelector)
    {
        _customerSelector = customerSelector;
    }

    public string MailMerge
    {
        get
        {
            Customer recipient =
                _customerSelector.CurrentCustomer;
            if (recipient == null)
            {
                return string.Empty;
            }
            else
            {
                string name = recipient.Name;

                return string.Format(
                    "Congratulations {0}, you are a winner!",
                    name);
            }
        }
    }
}

Listing 2 (VB): Mail merge using Update Controls

Public Class CRMPresentation
    Private _customerSelector As CustomerSelector

    Public Sub New(ByVal customerSelector As CustomerSelector)
        _customerSelector = customerSelector
    End Sub

    Public ReadOnly Property MailMerge() As String
        Get
            ' Calculate the new mail merge.
            Dim recipient As Customer
            recipient = _customerSelector.CurrentCustomer
            If recipient Is Nothing Then
                Return String.Empty
            Else
                Return String.Format( _
                    "Congratulations {0}, you are a winner!", _
                    recipient.Name)
            End If
        End Get
    End Property
End Class

Listing 3 (C#): A class with an independent property

public class CustomerSelector
{
    private Customer _currentCustomer;
    private Independent _indCurrentCustomer = new Independent();

    public Customer CurrentCustomer
    {
        get
        {
            _indCurrentCustomer.OnGet();
            return _currentCustomer;
        }
        set
        {
            _indCurrentCustomer.OnSet();
            _currentCustomer = value;
        }
    }
}

Listing 3 (VB): A class with an independent property

Public Class CustomerSelector
    Private _currentCustomer As Customer
    Private _indCurrentCustomer As New UpdateControls.Independent

    Public Property CurrentCustomer() As Customer
        Get
            _indCurrentCustomer.OnGet()
            Return _currentCustomer
        End Get
        Set(ByVal value As Customer)
            _indCurrentCustomer.OnSet()
            _currentCustomer = value
        End Set
    End Property
End Class