Archive for January, 2009

The Navigation Model Pattern

Monday, January 19th, 2009

View a video of this demo. Download version 2.0.3 of Update Controls and the demo source code to follow along.

Navigation Model Intent
The Navigation Model Pattern removes dependencies between view objects and makes UI state available to presentation logic.

Use the Navigation Model Pattern when controls interact with one another in a non-trivial manner. For example, selecting an object in a list displays details in a grid. Or checking a checkbox enables an associated control.

Problem
WPF makes it really easy to bind a property of one control to a property of another. For example, if the selected item in a list box becomes the data context for a grid, the code might look like this:

<ListBox ItemsSource="{Binding People}" x:Name="personListBox">
	<!-- ... -->
</ListBox>

<Grid DataContext="{Binding ElementName=personListBox, Path=SelectedItem}">
	<!-- ... -->
	<Label Grid.Row="0" Grid.Column="0" Content="First Name:"/>
	<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding First}"/>
	<!-- ... -->
</Grid>

But direct control-to-control data binding causes trouble. Your UI is no longer composable, since controls directly reference one another. It is difficult to perform presentation logic on control properties, since the presentation model would have a reverse dependency upon the view. And it is difficult to programmatically set a control property based on user action.

Solution
Instead of binding controls directly to one another, move all of the user selection state into a Navigation Model. The navigation model is one shared location where user selection state resides. All controls that use this shared state bind to this one place. The controls don't know about one another. Any control can set the selection state, and any control can consume it. When the selection changes, all controls are updated.

Create a navigation model class
The navigation model is just a class. It has properties that correspond to the user's current selections. It has no persistent storage for this state. It survives only as long as the user's session.

In the example that we've been building, the user can select a Person. The resulting navigation model looks like this:

public class NavigationModel
{
    private Person _selectedPerson;

    #region Independent properties
    // Generated by Update Controls --------------------------------
    private Independent _indSelectedPerson = new Independent();

    public Person SelectedPerson
    {
        get { _indSelectedPerson.OnGet(); return _selectedPerson; }
        set { _indSelectedPerson.OnSet(); _selectedPerson = value; }
    }
    // End generated code --------------------------------
    #endregion
}

To generate this class, declare just the field. Select the field and press Ctrl+D, G. The Update Controls add-in will generate the property and the Independent sentry.

Expose the navigation model through the presentation model
The presentation model is a thin, transparent wrapper around the data and navigation models. It adds presentation logic where necessary, but does not hide these raw models from the view.

The presentation model initializes a reference to the navigation model in its constructor, and exposes that reference as a property. It also uses that reference in other presentation properties.

public class PresentationModel
{
    private PersonList _personList;
    private NavigationModel _navigationModel;

    public PresentationModel(PersonList personList, NavigationModel navigationModel)
    {
        _personList = personList;
        _navigationModel = navigationModel;
    }

    public PersonList PersonList
    {
        get { return _personList; }
    }

    public NavigationModel NavigationModel
    {
        get { return _navigationModel; }
    }

    public string Title
    {
        get { return "People - " +

                (_navigationModel.SelectedPerson != null ?
                    _navigationModel.SelectedPerson.Name : ""); }
    }
}

References to the data model and presentation model are not generated using Ctrl+D, G. These models don't change, so there is no need to inject Independent sentries for change tracking.

Connect controls to the navigation model
The view can access navigation model properties through the presentation model's reference. Connect the SelectedItem property of the list box to the navigation model to allow the user to change it. Connect the DataContext property of the details grid to the navigation model so that it responds to user selection.

<ListBox ItemsSource="{u:Update PersonList.People}" SelectedItem="{u:Update NavigationModel.SelectedPerson}">
	<!-- ... -->
</ListBox>

<Grid DataContext="{u:Update NavigationModel.SelectedPerson}">
	<!-- ... -->
	<Label Grid.Row="0" Grid.Column="0" Content="First Name:"/>
	<TextBox Grid.Row="0" Grid.Column="1" Text="{u:Update First}"/>
	<!-- ... -->
</Grid>

We'll want some controls to become enabled only when conditions are right. To facilitate this, we add a boolean IsPersonSelected property to the navigation model. Be sure to use the SelectedPerson property, not the _selectedPerson field, so that we get the benefit of change tracking.

public class NavigationModel
{
    private Person _selectedPerson;

    #region Independent properties // ...

    public bool IsPersonSelected
    {
        get { return SelectedPerson != null; }
    }
}

Connect this property to the IsEnabled property of selected controls. For entire groups of controls, we wrap the group in a container, and connect the property of the container to the boolean. We can't use the existing container, because it changes its own data context.

<Button Content="Delete" IsEnabled="{u:Update NavigationModel.IsPersonSelected}" Click="DeleteButton_Click" />

<StackPanel IsEnabled="{u:Update NavigationModel.IsPersonSelected}">
	<Grid DataContext="{u:Update NavigationModel.SelectedPerson}">
		<!-- ... -->
	</Grid>
</StackPanel>

Consequences
While this pattern decouples view components to make them more composable, it does so at the cost of injecting code where once only markup was necessary. This means that it is difficult for a designer to express the behavior of an application without involving a developer.

To mitigate this cost, designers and developers should agree on a contract beforehand. Obvious properties, like SelectedPerson, should be added to the navigation model immediately. Less obvious properties, like IsPersonSelected, can be added afterward. It is very difficult to refactor in a navigation model after view components have been constructed, so the architecture should start with this pattern in place.

It is also troublesome that setting the DataContext of a control makes it impossible to get back to the presentation model. If detail controls need access to presentation logic or navigation state, then an additional presentation/navigation layer must be injected. This will be demonstrated in a future post.

Disable smart quotes in WordPress without requiring www prefix

Tuesday, January 6th, 2009

If you've attempted to visit this site without using the "deprecated" www prefix since the beginning of the year, you probably noticed that it wasn't coming up. I hope you noticed, because I didn't until this morning.

On December 31, I got tired of WordPress messing with my source code and changing all of the quotes. So I found instructions from Peter Cooper on disabling that feature. I took his advice and added the line "<?php remove_filter('the_content', 'wptexturize'); ?>" to the bottom of functions.php. A quick test showed that it worked. Hurray! I can copy and paste source code!

Fast forward to today, when I tried typed in http://adventuresinsoftware.com/blog and got a blank page. http://www.adventuresinsoftware.com/blog worked, as did http://adventuresinsoftware.com (the www. and /blog get added via redirect). So I go through my 5-step process: panic, research, try to fix, fail, and call for technical support. The folks at eApps pointed me in the right direction. They found this error message (which I had seen and disregarded, to my peril):

PHP Warning:  Cannot modify header information - headers already sent by (output started at /var/www/html/wordpress/wp-content/themes/ais2/functions.php:399) in /var/www/html/wordpress/wp-includes/pluggable.php on line 390

The problem is that Word Press is trying to perform a redirect to put the www back into the URL. In doing so, it ran up against the output that functions.php had already performed. The first output causes the HTTP header to be written, making it impossible to perform the redirect.

Here's my solution
Before doing anything else, I deleted the line that I had added to functions.php and tried the site again. It was back to its no-www-necessary behavior. But the troublesome "smart" quotes were back.

So the challenge was to find a way to remove the wptexturize filter without bringing down the site. I dusted off grep and took a tour through the code. I found the places in default-filters.php where that filter was being added. So instead of removing it, I just deleted those add_filter lines.

And so now, thanks to the help of Peter Cooper and eApps, I have copyable code and no www necessary. Now if I could only get Word Press to stop putting the www back into the URL for me.

Update
I found two places in the site options where I had www in front of the domain name. I fixed those, and now it redirects you away from www instead of toward it. So even that is fixed.