Presentation Model in WPF without INotifyPropertyChanged

View a video of this demo.

The Presentation Model pattern, otherwise known as Model/View/ViewModel, inserts an intermediate class between your data model and your view. This intermediate class implements presentation logic, behavior directly related to user interaction. This logic doesn't belong in the data model, because it is not related to business logic. Nor does it belong in the view, because the view is difficult to unit test, and is more concerned with style and layout.

Current implementations of Presentation Model in WPF rely on data binding, which requires you to implement INotifyPropertyChanged. While this is just a minor inconvenience when binding directly to data objects, it is particularly troublesome to a presentation model. Since a presentation model consumes properties of a data model, it must register for PropertyChanged events. And since the view binds to the presentation model rather than the data model, it must turn around and fire those events again.

Here's my solution
As I posted earlier, Update Controls replaces WPF data binding and does not require you to implement INotifyPropertyChanged. I've just published a new version of Update Controls (2.0.2), which adds a feature especially useful for the Presentation Model pattern. You can now use dotted identifier syntax to reference objects indirectly. Now the presentation model doesn't have to pass through all of the properties of the data model. It only needs to implement those that it intends to tweak for the UI, and expose a reference to the data model for access to all the others.

Wrap the data object
In the previous post, we created a Person data object and some XAML that bound to it. We'll keep the same Person object, but wrap it in a PersonPresentation object. This object exposes the Person as a property, and adds UI logic for the window title.

public class PersonPresentation
{
    private Person _person;

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

    public Person Person
    {
        get { return _person; }
    }

    public string Title
    {
        get { return "Person - " + _person.Display; }
    }
}

Instead of directly using Person as the DataContext for the window, use a PersonPresentation.

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new PersonPresentation(new Person());
    }
}

Now use dotted-identifier syntax in the XAML to access the Person for data properties. But for UI properties, like the window title, go directly after the PersonPresentation object.

<Window x:Class="UpdateControls.XAML.Test.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:u="clr-namespace:UpdateControls.XAML;assembly=UpdateControls.XAML"
    Title="{u:Update Title}" Height="300" Width="300">
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label Content="First Name:" Width="100"/>
            <TextBox Text="{u:Update Person.FirstName}" Width="170"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label Content="Last Name:" Width="100"/>
            <TextBox Text="{u:Update Person.LastName}" Width="170"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label Content="Display As:" Width="100"/>
                <ComboBox SelectedIndex="{u:Update Person.DisplayStrategy}" Width="170">
                    <ComboBoxItem Content="{u:Update Person.FirstLast}" />
                    <ComboBoxItem Content="{u:Update Person.LastFirst}"/>
                </ComboBox>
        </StackPanel>
    </StackPanel>
</Window>

With the Presentation Model pattern, you achieve separation of concerns. Business logic is encapsulated in the data model, style and layout are in the view, and interaction logic is in the presentation model.

Leave a Reply

You must be logged in to post a comment.