Update Controls in WPF and Silverlight

Update Controls .NET is a library of controls for Windows Forms that automatically update themselves. They are superior to data binding because they can see through layers of business logic and respond to changes in the underlying data. It is not necessary to implement INotifyPropertyChanged, or to bubble up state-change events by any means.

In WPF, and now in Silverlight 2.0, Microsoft has provided a significant improvement to data binding. It is now easy to add data binding to any user control. Data binding can now be expressed declaratively within XAML. And it is trivially simple for one dependency property to depend upon another one. The improvements are so great that it is difficult for people to see the pain that is still there.

INotifyPropertyChanged and ObservableCollection<T>
In WPF and Silverlight data binding, it is still necessary for the developer to fire property changed events. These events are fired by an object that implements INotifyPropertyChanged. The interface makes it possible for dependency properties to register for these events.

Some helper classes implement this interface on your behalf. For example, ObservableCollection<T> is a generic container that fires property changed events when items are added or removed. By exposing these helper classes from your own business class, you can offload the responsibility of notification.

Typically, you use ObservableCollection<T> in your data objects. If you bind your user interface directly to your data objects, then everything works just fine. But if you want a layer of business logic between the data layer and the UI, ObservableCollection<T> is no longer helpful to you. What you want is an intermediate collection that can filter the source, or wrap adapters around each of the items. ObservableCollection<T> only provides a way to bind directly to the source collection without modification.

Presentation Model pattern
The Model/View/ViewModel pattern was created by the Blend team to separate user interface logic from business logic. In this pattern, the view model transforms the raw data from the model into the visible data shown to the user. The view model observes the underlying data model and notifies the view when it changes.

Martin Fowler documents a similar pattern called the Presentation Model. In his document, the role of the ViewModel is played by the Presentation Model. I find this a more descriptive term, so it is the one I adopt. He calls out synchronization between the presentation model and view is the most annoying part of the pattern. While .NET data binding attempts to solve this problem, it still requires that the presentation model subscribe to notifications from the model and forward them to the view.

Update Controls Presentation Model
I am currently working on full support of the Presentation Model pattern in Update Controls. Since the strong point of Update Controls is tracking dependency through layers of business logic, it closes this annoying gap in Fowler's pattern. I will be releasing source code shortly, but here's what I have so far:

    public class PersonPresentation : PresentationObject<Person>
    {
        private PresentationProperty<string> _firstName;
        private PresentationProperty<string> _lastName;
        private PresentationProperty<string> _firstLast;
        private PresentationProperty<string> _lastFirst;
        private PresentationProperty<int> _displayStrategy;
        private PresentationProperty<string> _title;

        public PersonPresentation(Person person) :
            base(person)
        {
            _firstName = new PresentationProperty<string>(this, "FirstName",

                () => BusinessObject.FirstName);
            _lastName = new PresentationProperty<string>(this, "LastName",
                () => BusinessObject.LastName);
            _firstLast = new PresentationProperty<string>(this, "FirstLast",
                () => CalculateFirstLast());
            _lastFirst = new PresentationProperty<string>(this, "LastFirst",
                () => CalculateLastFirst());
            _displayStrategy = new PresentationProperty<int>(this, "DisplayStrategy",
                () => BusinessObject.DisplayStrategy);
            _title = new PresentationProperty<string>(this, "Title",
                () => "Person - " + (BusinessObject.DisplayStrategy == 0 ? CalculateFirstLast() : CalculateLastFirst()));
        }

        private string CalculateLastFirst()
        {
            return BusinessObject.LastName + ", " + BusinessObject.FirstName;
        }

        private string CalculateFirstLast()
        {
            return BusinessObject.FirstName + " " + BusinessObject.LastName;
        }

        public string FirstName
        {
            get { return _firstName.Value; }
            set { BusinessObject.FirstName = value; }
        }

        public string LastName
        {
            get { return _lastName.Value; }
            set { BusinessObject.LastName = value; }
        }

        public int DisplayStrategy
        {
            get { return _displayStrategy.Value; }
            set { BusinessObject.DisplayStrategy = value; }
        }

        public string FirstLast
        {
            get { return _firstLast.Value; }
        }

        public string LastFirst
        {
            get { return _lastFirst.Value; }
        }

        public string Title
        {
            get { return _title.Value; }
        }
    }

The PersonPresentation object (presentation model) wraps the Person business object (data model). The PresentationObject base class implements INotifyPropertyChanged on your behalf. PresentationProperty is a wrapper around a data type that calls a provided Action when the property is out-of-date. You expose the value of each presentation property through a CLR property getter, and pass the setter through to the underlying business object. This allows you to specify attributes like "{Binding FirstName}" in your WPF or Silverlight.

Collections are handled in a similar fashion:

    public class PresentationAddressBook : PresentationObject<AddressBook>
    {
        private PresentationCollection<Person, PresentationPerson> _people;

        public PresentationAddressBook(AddressBook addressBook) :
            base(addressBook)
        {
            _people = new PresentationCollection<Person, PresentationPerson>(this, "People",
                () => BusinessObject.People,
                person => new PresentationPerson(person));
        }

        public ICollection<PresentationPerson> People
        {
            get { return _people.Value; }
        }

        public void AddPerson()
        {
            BusinessObject.AddPerson();
        }
    }

The PresentationCollection performs two actions rather than just one. It first gets the collection of business objects from the parent business object. Then, it wraps each business object in another presentation object. In the process, it performs object recycling so that you reuse a presentation object for a given business object.

Coming soon
I am still refining this mechanism. When it is finished, it will be part of the new Update Controls .NET 2.0 release. I would like to reduce the amount of code necessary to write a presentation object. Ideally, you would need no base class and no members (besides the business object reference). A presentation model should be as simple as this:

    public class PersonPresentation
    {
        private Person _person;

        public PersonPresentation(Person person)
        {
            _person = person;
        }

        public string FirstName
        {
            get { return _person.FirstName; }
            set { _person.FirstName = value; }
        }

        public string LastName
        {
            get { return _person.LastName; }
            set { _person.LastName = value; }
        }

        public int DisplayStrategy
        {
            get { return _person.DisplayStrategy; }
            set { _person.DisplayStrategy = value; }
        }

        public string FirstLast
        {
            get { return _person.FirstName + " " + _person.LastName; }
        }

        public string LastFirst
        {
            get { return _person.LastName + ", " + _person.FirstName; }
        }

        public string Title
        {
            get { return "Person - " + (DisplayStrategy == 0 ? FirstLast : LastFirst); }
        }
    }

That's my goal. Let's see how close I get.

Leave a Reply

You must be logged in to post a comment.