List and detail

Many applications use the list/detail UI idiom. The user selects an item in a list. The item appears in a detail pane. This is easy to do in Update Controls using the Navigation Model pattern.

List property

First, we need a property in our view model that returns a list of items. Each item will itself be a view model. We wrap each data model object in a summary view model using a linq select statement.

public IEnumerable<OrderSummaryViewModel> PendingOrders
{
    get
    {
        return
            from o in _company.PendingOrders
            select new OrderSummaryViewModel(o);
    }
}

Next, we data bind this property to a list box. Bind ItemsSource to the list. Use an ItemTemplate to render each item.

<ListBox ItemTemplate="{DynamicResource OrderSummaryViewModelTemplate}" ItemsSource="{Binding PendingOrders}"/>

Selected item property

Then, we need a property for the selected item. This property will be the same type as the items in the list.

public OrderSummaryViewModel SelectedOrderSummary
{
    get
    {
        return _navigation.SelectedOrder == null
            ? null
            : new OrderSummaryViewModel(_navigation.SelectedOrder);
    }
    set
    {
        _navigation.SelectedOrder = value == null
            ? null
            : value.Order;
    }
}

Since the first property was IEnumerable of OrderSummaryViewModel, the second needs to be OrderSummaryViewModel. However, we don’t want to store the view model type in the navigation model. Instead, we store the data model.

private Order _selectedOrder;

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

public Order SelectedOrder
{
    get { _indSelectedOrder.OnGet(); return _selectedOrder; }
    set { _indSelectedOrder.OnSet(); _selectedOrder = value; }
}
// End generated code --------------------------------
#endregion

Bind this new property to the SelectedItem of the ListBox.

<ListBox ItemTemplate="{DynamicResource OrderSummaryViewModelTemplate}" ItemsSource="{Binding PendingOrders}" SelectedItem="{Bidning SelectedOrderSummary}"/>

Equals and GetHashCode

Now the trick is that the selected order has to be equal to one of the items in the list. Since we are creating new OrderSummaryViewModels in each of these properties, they will never be the same object. So we have to implement Equals ourselves.

public override bool Equals(object obj)
{
    if (obj == this)
        return true;
    OrderSummaryViewModel that = obj as OrderSummaryViewModel;
    if (that == null)
        return false;
    return this._order == that._order;
}

Whenever we implement Equals, we must also implement GetHashCode.

public override int GetHashCode()
{
    return _order.GetHashCode();
}

Detail property

Finally, we can expose the selected order as a detail property. We wrap the order in a different view model, one specifically tailored to editing.

public OrderDetailsViewModel SelectedOrderDetails
{
    get
    {
        return _navigation.SelectedOrder == null
            ? null
            : new OrderDetailsViewModel(_navigation.SelectedOrder);
    }
}

The benefit of this pattern is that you can change the selection programmatically without talking directly to the view. Both the selection and the details will update. I like to set the selected order to a new order when it is first created. Sometimes there are other controls that need to bind to or change the selection themselves. For example, a button might delete the selected item. Simply bind those other controls to the same navigation model, and they will all work together.

Comments

Context & Correctness

Have you noticed that you often introduce a code snippet without saying where it is supposed to go in the application? Being new to the "Update Controls Way", I needed to think awhile to figure out that SelectedOrderSummary belongs in the top-level viewmodel (whatever that's called--you did not give it a name).

The implicit context for this post is some sort of "customers/orders" application, which seems to be a common idiom in Microsoftland, but personally I have never written or worked on this type of app. I encourage you to provide more context in your examples... and write your code in a temporary project to avoid mistakes (even if you throw away the project without posting it); because I noticed a typo in the XAML: SelectedItem="{Bidning SelectedOrderSummary}"

In any case, I am loving UpdateControls so far!

Good points

That's the new "Bidning" markup extension. You like it? :)

Yes, I need to get into the habit of posting the full source code, and make it clearer where things lie. I have full source on a few examples, but I usually favor terseness over completeness in the explanations.

And, yes, I'm getting tired of the Person, FirstName, LastName example. I'll be a bit more creative in future posts.