Home > Navigation > WPFGlue Navigation

WPFGlue Navigation

Let me explain why WPF navigation could do with a little glue:

WPFGlue is about connecting a business object model to a user interface written in WPF, or to be more precise, entirely in XAML. So, let’s imagine the business object model is already there. To take the classic example: you’d have a Customer class that has an Orders collection with a couple of Order objects in it. In the application, you’d probably want to display a list of Customers, click on one to go to a new page that displays its details, including a link to this customers orders, which displays a List of Orders page, which leads to an Order Detail page etc. So, when you navigate from one page to the next, you need two bits of information: the URL of the page you want to go to, and the object or collection that the page should display.

The Object Selection Problem

The WPF documentation suggests passing objects to pages through constructors. This is safe but very unsticky, since it requires code behind. Moreover, pages that have been instantiated as objects and have not been called through a URL will not be freed when the user navigates away, but will be stuck in memory until the application shuts down. This makes great sense, because only by staying alive itself and holding on to its references to the business objects the page can be sure that the business objects still will be there when the user navigates back to it. However, it costs memory, and keeping business objects alive is a side effect that might not be appreciated. After all, the business object thinks it is responsible for its objects and collections, and finding an object still alive while the business object model doesn’t know it any more might have unwelcome effects.

So, this problem calls for two solutions: how to pass a selected object from one page to another while still using an URL, and how to handle object references while the user navigates back and forth through the journal.

The idea for solving the first problem is to create a combination of Sticky Properties, Sticky Behaviours and a Sticky CommandBinding. The CommandBinding would handle the NavigationCommands.GoToPage command, look for an URL and an object reference in Sticky Properties on the element that originated the command, and use the NavigationService.Navigate(URI,Object) method to navigate to the next page. The Sticky Behaviours would attach themselves to the Navigating and LoadCompleted events of the NavigationService and set the DataContext of the new page to the object that would be passed forward.

The Backward Navigation Problem

The idea for solving the second problem is to have a backup storage for object references, which can be accessed by index and restored when the user navigates backwards or forwards.

The mechanism for storing custom data in the Journal is a class called CustomContentState. By deriving from this class, one can add information to the journal that will be restored automatically when the user navigates forwards or backwards. Now, if one tries to add object references to a CustomContentState implementation, one finds out that these objects need to be serializable. What CustomContentState does is that it serializes itself, stores itself in the journal in serialized form, and is deserialized before the page is displayed again.

It is understandable why this is so: By serializing the data, one avoids having lots of unresolved object references in the journal, keeping objects from being freed and creating the same kind of side effect that is created by keeping pages alive. So, what can one do?

I decided to use WeakReferences to solve this problem. The WeakReferences are stored in a dictionary with an auto-incrementing number as index. When the user navigates away from a page, the system stores the DataContext in the dictionary and saves the index to this reference in the CustomContentState, which is retained by the Journal. When the user returns to the page, the CustomContentState tries to retrieve the DataContext using the index and the WeakReference to the object. If at this point the object has already been garbage collected, the WeakReference will show this. In this case, the CustomContentState will trigger a navigation further backwards, since the data of the page obviously has expired. If the data on all the preceding pages has expired, the application will end up on the start page, which must be able to create its own DataContext anyway (if any).

However, there is one drawback to WeakReferences: they require full trust, so they will probably not work in XBAP applications. I want to research that at some stage in the future.

The Navigation Topology Problem

It’s possible to use a hyperlink and to specify the URL of the page immediately in XAML. However, doing this means that the relationships between the pages are hardcoded in the pages themselves, and the sequence of pages and their links between each other, i.e. the navigation topology of the application, are distributed throughout all the pages. This is not very maintenance friendly, and it makes it more difficult to reuse pages in other places in an application.

The first solution to this would be to avoid URLs in the pages, and instead use a global navigation menu that is defined in a central location. The WPFGlue pattern for this would be a ViewModelKit, providing classes for Menus and MenuItems that can be configured in a resource dictionary. The View for the menu would then use this definition as ViewModel, and use it to create hyperlinks or navigation buttons. These in turn would rely on URLs set on the menu items in the resource dictionary.

Categories: Navigation Tags: , ,
  1. No comments yet.
  1. December 8, 2009 at 16:52

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: