WPF Data Binding: Creating a Master-Details form in Visual Studio 2010

Visual Studio 2010 Beta is released. Late last year we had released a Community Technology Preview (CTP) of VS2010. At that time I had written about some improvements to the data binding improvements for WPF. The Beta has many more and we will discuss them on this blog in a series of posts.

In my blog post last November I wrote about the newly added support for drag-drop data binding for WPF and that, like DataSets, EDM is one of the data sources supported by the data sources window. This post is about building a master-details form in WPF against an Entity Data Model. We will talk about the user experience, but more importantly about what code/XAML gets generated – at least the aspects that are relevant to master-details data binding.

The experience is very similar to WinForms over DataSets. Let us take the example of two tables: Customers and their Orders and create a form that lists the names of customers and for each customer, their orders.

As described in my earlier post, we can add an EDM to our WPF project and have it contain the two corresponding entities.

image

Here’s what you would see in the data sources window:

image

Here is how we can create the master details form:

  1. From the tool box drag-drop a ListBox onto the form.
  2. From the data sources window drag-drop the LastName column of Customer entity onto the ListBox
  3. Drag-drop the Order Entity from under the Customer node beside the ListBox on the form.

That’s it, really. You can press F5 to run the application and you will see your basic master-details form working. The customers list box will show a list of customer names and for the selected name the orders DataGrid will show their orders.

image

First, if you look at the XAML that gets generated, under Windows.Resources you will find two CollectionViewSource entries: One for Customers and one for Orders. These serve as the binding components (like the BindingSource objects in WinForms).

<CollectionViewSource x:Key="CustomersViewSource__OMSEntities" d:DesignSource="{d:DesignInstance my:Customer, CreateList=True}" />

<CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource CustomersViewSource__OMSEntities}}" />

But note that the source for the CustomersViewSource__OMSEntities is the Customer entity, where as the source for CustomersOrdersViewSource is the CustomersViewSource__OMSEntities. Thus there is a master-details relationship between the two CollectionViewSouce objects. Now customer list is bound to CustomersViewSource__OMSEntities and the orders DataGrid is bound to CollectionViewSouce.

One more thing: We set the IsSynchronizedWithCurrentItem property of the customers list box to True. That way every time you change the selection in the listbox the current item in the CollectionViewSource is updated.

Second, if you look at the code behind the form, you will find that code to load the data for the entities is generated in the form’s Loaded event handler. In this code we build the object query for Customers and their orders, execute the query and assign the results to the source property of the CollectionViewSource of the Customers entity.

We construct the query in the GetCustomersQuery method. The basic query that loads customers and their objects is:

Dim

CustomersQuery As ObjectQuery(Of Customer) = OMSEntities.Customer.Include("Orders")

However normally you would want to filter the query, and return only the subset of the results that are relevant. Modify the query in the GetCustomersQuery method to include your Where condition.