Create Dynamic XAML Forms with the Presentation Model Pattern

his article presents an advanced technique that lets you bind multiple editable line items to a collection using Windows Presentation Foundation and the Presentation Model pattern. It assumes you are familiar with basic WPF data binding techniques as well as design patterns that object-oriented UI libraries typically use. After an introduction to the sample application used throughout this article, you’ll see how applying the Presentation Model pattern insulates this application’s UI and business logic layers from one another. Finally, you’ll see the WPF-specific details involved in binding a Presentation Model to XAML controls to create a dynamic UI with multiple editable line items.

A Dynamic User Interface
To illustrate the meaning of “a dynamic UI with multiple editable line items,” it’s useful to begin with a contrasting example?a static interface. In this use case, a web-based account registration form asks users for personal information, and maps their answers to a corresponding business object or database table, which represents a user profile. After users fill out and submit the form, the site stores their personal information, and sends a confirmation e-mail, which completes the use case. Assuming the UI and validation code is correct, consider that the developer who wrote the form knew the exact information needed in advance, and could therefore constrain users from entering invalid data; in other words, the interface is static?validation paramters for the requested information are known in advance, and the UI doesn’t change from one instance of the application to the next.

In contrast, consider a transaction register that lets you track day-to-day expenses. In this form, users might enter one expense or twenty. The developer in charge of writing such an application could play it safe by displaying some large preset number of empty line items (more than anyone would want to enter for a single day), but that would be a less than elegant approach. A better alternative would be a UI that generates new line items on demand. This is a dynamic UI because the developer cannot know the exact volume of information (the number of items) in advance. The dynamic UI approach is more intuitive, minimizes on-screen clutter, and lets users focus on the line items at hand by adjusting the number of items to suit the immediate need.

A Sample Application: Expenses.NET
The sample application for this article, “Expenses.NET,” is exactly such a dynamic-UI transaction register (see Figure 1). It lets you itemize expenses day by day within a weekly period. Upon launch, the center of the UI displays a ListView with a single ListViewItem. The ListViewItem contains a ComboBox to select the weekday and date of each expense, a TextBox to enter a description of the expense, another TextBox to enter an amount, and two buttons that allow users to add additional line items or delete existing items. A business object called ExpenseLineItem backs each ListViewItem. The ExpenseLineItem class exposes properties corresponding to each of the controls listed above, and implements the INotifyPropertyChanged interface to work with the WPF binding framework. Another business object called ExpenseSheet backs the ListView itself. ExpenseSheet contains an ObservableCollection of ExpenseLineItems.

?
Figure 1. Expenses.NET in Action: The screen capture shows the sample application with a single filled-out expense.
?
Figure 2. The Presentation Model: This model uses data from business objects to drive the state of the entire UI.

The Presentation Model Pattern
The Presentation Model pattern (see Figure 2) lies at the heart of Expenses.NET. You won’t find a chapter on this pattern in the classic Gang of Four Design Patterns book (Design Patterns: Elements of Reusable Object-Oriented Software), but Martin Fowler has devoted a page to it on the Enterprise Application Architecture section of his web site. To quote Fowler, the pattern is a way to “represent the state and behavior of the presentation independently of the GUI controls used in the interface.” You might be wondering how that differs from MVC or similar patterns that UI control libraries typically use?and, it doesn’t really; it is an application-level abstraction above the controls that uses data from business objects to drive the state of the entire user interface. The properties of the business objects correspond to properties on the controls. In this case, WPF data binding is the glue that keeps them synchronized. This is a powerful abstraction, because it isn’t platform-specific; you can reuse it against other UIs targeted toward technologies such as ASP.NET web applications or mobile devices.

A Concrete Example
To create a dynamic UI backed by a Presentation Model, you will define a ListView that uses DataTemplates to bind elements in a collection to ListViewItems. After those components are working together, adding and removing line items becomes a simple matter of calling Presentation Model methods from UI event handlers. Before all this can come together though, you need to introduce the Presentation Model into the XAML UI.

ExpenseSheetWindow is the XAML UI for Expenses.NET. The corresponding Presentation Model is ExpenseSheetWindowModel. You create an ObjectDataProvider in the root Window element’s Resources block, and then bind it to the Window’s DataContext. Now the Presentation Model and its properties are available to all controls:

   public class ExpenseSheetWindowModel : INotifyPropertyChanged {      private ExpenseSheet currentExpenseSheet;      public event PropertyChangedEventHandler PropertyChanged;         public ExpenseSheet CurrentExpenseSheet {         get { return currentExpenseSheet; }      }         private void NotifyPropertyChanged(string info) {         if (PropertyChanged != null) {            PropertyChanged(this, new                PropertyChangedEventArgs(info));         }      }   }

Here’s the basic XAML code:

                                                               

Now you’ll create the ListView that displays the line items. You might be wondering what the benefit is of using a ListView over its parent, ItemsControl?after all, ListView derives from Selector, a class that provides functionality you don’t need (for example, you don’t want any of the line items to highlight when selected). But ListView also has a specialty view called a GridView that arranges controls in a grid, with column headers over each column that users can rearrange as desired. That adds a nice look and feel for Expenses.NET. While you could dig into the internals of the ItemsControl class to try to make it work with GridView, it’s easier to use a ListView and suppress its selection functionality:

                                                                  

With the ListView defined, you need to hook it up to the ExpenseSheetWindowModel by setting its ItemsSource property to a collection nested within the Presentation Model. ExpenseSheetWindowModel maintains a reference to the current ExpenseSheet for a given weekly period. Recall that an ExpenseSheet contains an ObservableCollection of ExpenseLineItems; this will be your source property into the binding:

   

If you launch Expenses.NET at this point, you’ll notice it comes up with an empty ListView. But you want it to display one initial ExpenseLineItem so users have a place to start entering expenses. The ObjectDataProvider you defined implicitly calls ExpenseSheetWindowModel’s default constructor. This is a good place to initialize an empty line item:

   public ExpenseSheetWindowModel() {      currentExpenseSheet = new ExpenseSheet(DateTime.Now);      CurrentExpenseSheet.LineItems.Insert(0, new ExpenseLineItem());   }

Interestingly enough, launching the application again still yields what appears to be an empty ListView. Don’t be fooled though?the apparently empty ListView contains an empty ExpenseLineItem. The default presentation of the ListViewItem renders the output of the ExpenseLineItem’s ToString method in a TextBlock. Because the implementation returns the description property (which is initially set to null), the ListViewItem isn’t visible. However, you can see something is in fact there by clicking on the space in which it should appear, and watching it highlight.

To hold the data for each ExpenseLineItem, you’re going to replace each ListViewItem’s default, read-only TextBlock by using DataTemplates to substitute editable controls. A DataTemplate lets you take advantage of WPF’s rich content model by applying a user-defined presentation to each item in a collection as it’s rendered. The first three DataTemplates are each bound to a property of the ExpenseLineItem implicitly passed in from the ListView’s ItemsSource. The last DataTemplate contains two buttons to add or remove ExpenseLineItems. You’ll wire these up later to events in the XAML code-behind:

                                                                                                              

These four DataTemplates correspond to the four GridViewColumns you defined earlier. You wire the DataTemplates and GridViewColumns together by setting each GridViewColumn’s CellTemplate property:

                          

Finishing Up
Your ListView definition is essentially complete, but to keep this article compact, I’ve left out an important part; you’ll have to dig into the included sample code to see it. That omitted code uses another DataTemplate with an IValueConverter to change the date formatting on the startDateTemplate‘s ComboBox.

With the ListView defined, you can add event handlers to the XAML code-behind that delegate the process of adding and removing ExpenseLineItems to ExpenseSheetWindowModel. You need to know the index of the ListViewItem the user wants to add or remove (the index of the item where the user clicked the ‘Add’ or “Delete” button), so you can insert a line item after that index or remove the line item at that index. In the sample code, the addButton_Click and deleteButton_Click event handlers walk up the visual tree to locate the ListViewItem and pass it to the ListView’s ItemContentGenerator method to get the index:

Author’s Note: Josh Smith, MVP helped me find the index of ListViewItems in this thread.

   private void addButton_Click(      object sender, RoutedEventArgs e) {      int index = GetListViewItemIndex(         e.OriginalSource as DependencyObject);      presentationModel.InsertRowIntoCurrentExpenseSheet(         index + 1, new ExpenseLineItem());   }      private void deleteButton_Click(      object sender, RoutedEventArgs e) {      ItemCollection items = lineItemListView.Items;         // don't delete last item      if (items.Count == 1) { return; }          int index = GetListViewItemIndex(         e.OriginalSource as DependencyObject);      presentationModel.RemoveRowFromCurrentExpenseSheet(index);   }      private int GetListViewItemIndex(DependencyObject depObj) {      while (!(depObj is ListViewItem)) {         depObj = VisualTreeHelper.GetParent(depObj);   }      return lineItemListView.ItemContainerGenerator.      IndexFromContainer(depObj);   }

The corresponding ExpenseSheetWindowModel methods simply modify the ExpenseSheet’s ObservableCollection of ExpenseLineItems. Because this is an ObservableCollection, modifications fire PropertyChanged events and the UI redraws itself:

   public void RemoveRowFromCurrentExpenseSheet(int index) {      CurrentExpenseSheet.LineItems.RemoveAt(index);   }      public void InsertRowIntoCurrentExpenseSheet(      int index, ExpenseLineItem lineItem) {      CurrentExpenseSheet.LineItems.Insert(index, lineItem);   }

Extending the Principles
In this article, you have seen how the Presentation Model pattern aids in building dynamic user interfaces with multiple editable line items. As you examine the included sample code, you’ll see how ExpenseSheetWindowModel drives other parts of the UI, not discussed here, such as the navigation (the green arrow and square buttons in the top left in Figure 1). You can build upon the ideas shown here to develop UIs that might not necessarily display line items but that require multiple editable components with different layouts to capture unbounded sets of data.

The Presentation Model pattern lets you encapsulate the state of a user interface and its behavior. One could argue the method bodies for this example are small enough to fit inside the XAML code-behind and that the Presentation Model adds an unwarranted layer of complexity. For the small case above that might be true, but as you build larger UIs, the logic can quickly become unwieldy. When coupled with other chores such as launching background threads for long operations, setting the state of the mouse cursor, and displaying progress indicators and message boxes, code gets muddled and starts to rot. The Presentation Model cleanly ties the UI to business objects in an abstract way, so you can build completely different UI’s in the future without having to re-implement their core logic.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

your company's audio

4 Areas of Your Company Where Your Audio Really Matters

Your company probably relies on audio more than you realize. Whether you’re creating a spoken text message to a colleague or giving a speech, you want your audio to shine. Otherwise, you could cause avoidable friction points and potentially hurt your brand reputation. For example, let’s say you create a

chrome os developer mode

How to Turn on Chrome OS Developer Mode

Google’s Chrome OS is a popular operating system that is widely used on Chromebooks and other devices. While it is designed to be simple and user-friendly, there are times when users may want to access additional features and functionality. One way to do this is by turning on Chrome OS

homes in the real estate industry

Exploring the Latest Tech Trends Impacting the Real Estate Industry

The real estate industry is changing thanks to the newest technological advancements. These new developments — from blockchain and AI to virtual reality and 3D printing — are poised to change how we buy and sell homes. Real estate brokers, buyers, sellers, wholesale real estate professionals, fix and flippers, and beyond may