Today I thought I would talk about a really common scenario in data applications, creating a master-details (one-to-many) data entry form. I’ve written about WPF data binding and Entity Framework a lot in the past:
Videos:
Today I want to pull these concepts together and walk through one way to create a master-detail form in WPF using entities from the Entity Framework. Specifically, we’ll declare CollectionViewSources in our XAML like I showed here, to bind to an ObservableCollection of entities like I showed here, where the children are explicitly loaded like I showed here. Everybody got that? ;-)
Creating the Entity Data Model
First let’s create a simple Entity Data Model (EDM) that demonstrates a Master-Detail relationship. I’ll use a simple database called OMS that has Customer and Orders tables with a non-nullable foreign key set up between them on CustomerID, meaning that no Order can exist without a Customer. This relationship is inferred by Entity Framework (EF) to set up the navigation properties. Notice that there is an Orders EntityCollection on Customer.

What we want to do is build a simple form that will let us Edit, Add, and Delete Customers and their Orders. First let’s set up the WPF Data Binding in XAML.
Defining the CollectionViewSource and Data Bindings
To recap, a CollectionViewSource is a proxy for the CollectionView which manages the currency (the position) in the list of entities. It has a property called Source which can be set in our code behind. This way, we can set up CollectionVieSources in XAML for all our data lists and bind them to the corresponding controls all in XAML. Then at runtime in our code we set the Source properties and only at that time does the data pull from the database.
To define a Master-Detail relationship we define two CollectionViewSources one for the master and one for the detail collections. Then on the detail we set the Source property to the master CollectionViewSource and then specify the Path property as the name of the child collection. In our case the name of the collection on Customer is “Orders”. So we can specify the XAML like so:
<Window.Resources>
<CollectionViewSource x:Key="MasterViewSource" />
<CollectionViewSource x:Key="DetailsViewSource"
Source="{Binding Source={StaticResource MasterViewSource}, Path='Orders'}" />
</Window.Resources>
Now as the position changes in the MasterViewSource to point to a new Customer, the DetailsViewSource will filter automatically to only those related Orders for that Customer. We can now set the rest of the data bindings on the controls on the form by setting the BindingContext of the container controls to the CollectionViewSource we want to display. For example, we can set up a StackPanel to contain the Customer fields and set the StackPanel.DataContext to the MasterViewSource. Under that we can set up a ListView to display the Orders by setting the ListView.ItemsSource to the DetailsViewSource.
<Grid>
...
<StackPanel Name="StackPanel2"
Grid.Column="1"
DataContext="{Binding Source={StaticResource MasterViewSource}}">
<TextBox Name="TextBox1" IsReadOnly="True"
Text="{Binding Path=CustomerID, Mode=OneWay}"/>
<TextBox Name="TextBox5"
Text="{Binding Path=LastName}"/>
...
</StackPanel>
...
<ListView Grid.Row="3" Name="ListView1"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource DetailsViewSource}}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" Width="75">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=OrderID}"
Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Order Date" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=OrderDate}"
Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
The only thing we need to do now is set the Source property of the MasterViewSource in code to the collection of our Customer entities.
Defining the Master-Detail Entities in an ObservableCollection
I showed before how we can create a collection of entities that inherits from ObservableCollection in this post to make it easier to work with WPF data binding. But in that example we were only working with a simple collection of Customers and not their Orders. If you recall, the ObjectContext is what tracks changes on entities so in order for the ObjectContext to be notified that adds and deletes to the ObservableCollection need to be tracked you need to override the InsertItem and RemoveItem methods so that you can tell the ObjectContext to either add or delete the entity which will ultimately execute against the database. In the constructor I pass a reference to the ObjectContext. You can also pass in any collection of entities, say from a LINQ query, and then add them to the ObservableCollection. However, we need to make a couple modifications to our collection so that we can also track the child order entities correctly.
Adds to the Customer.Orders EntityCollection will will cause the addition of a new Order to the collection as well as the association to Customer automatically. However removing the Order from the Customer.Orders EntityCollection will only remove the association and will not attempt to actually delete the Order from the database. Instead it attempts to set the CustomerID to NULL (to remove the association from the Customer) but since we have referential integrity set up to disallow this we will get an error if we attempt to SaveChanges.
In a lot of scenarios it makes sense to just remove the association and set the foreign key to NULL in the database. But in this example we really mean to delete the Order record completely when the Order is removed from the collection. So the key is adding an event handler to the AssociationChanged event on the Orders EntityCollection that’s hanging off our Customer entity and telling the ObjectContext to explicitly delete the Order.
Public Class CustomerCollection
Inherits ObservableCollection(Of Customer)
Private _context As OMSEntities
Public ReadOnly Property Context() As OMSEntities
Get
Return _context
End Get
End Property
Sub New(ByVal customers As IEnumerable(Of Customer), ByVal context As OMSEntities)
MyBase.New(customers)
_context = context
For Each c In customers
AddHandler c.Orders.AssociationChanged, AddressOf Orders_CollectionChanged
Next
End Sub
Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As Customer)
AddHandler item.Orders.AssociationChanged, AddressOf Orders_CollectionChanged
'Tell the ObjectContext to start tracking this customer entity
Me.Context.AddToCustomers(item)
MyBase.InsertItem(index, item)
End Sub
Protected Overrides Sub RemoveItem(ByVal index As Integer)
Dim customer = Me(index)
RemoveHandler customer.Orders.AssociationChanged, AddressOf Orders_CollectionChanged
For i = customer.Orders.Count - 1 To 0 Step -1
'When deleting a customer, delete any orders if any exist
Me.Context.DeleteObject(customer.Orders(i))
Next
'Tell the ObjectContext to delete this customer entity
Me.Context.DeleteObject(customer)
MyBase.RemoveItem(index)
End Sub
Private Sub Orders_CollectionChanged(ByVal sender As Object, _
ByVal e As CollectionChangeEventArgs)
If e.Action = CollectionChangeAction.Remove Then
'Adding an order to a customer is handled automatically
' for us but we need to tell the ObjectContext to delete the order
' if an order is removed from the Orders EntityCollection
Me.Context.DeleteObject(CType(e.Element, Order))
End If
End Sub
End Class
Loading the Master-Detail Entities
Finally we’re ready to write a LINQ query to load the entities into our CustomerCollection and then set that as the Source property of the MasterViewSource. In this example I’m loading the Orders explicitly by calling .Include(“Orders”) on the LINQ query which constructs a single statement to retrieve the Customer and all their Orders from the database. I discuss explicit load in this post.
We can then grab a reference to the MasterViewSource & DetailViewSource’s View property in order to add/remove items in the collections. When we’re done, we can call SaveChanges on the ObjectContext and the database will be updated.
Private db As New OMSEntities 'EF ObjectContext connects to database and tracks changes
Private CustomerData As CustomerCollection 'inherits from ObservableCollection
Private MasterViewSource As CollectionViewSource
Private DetailViewSource As CollectionViewSource
'provides currency to controls (position & movement in the collections)
Private WithEvents MasterView As ListCollectionView
Private DetailsView As BindingListCollectionView
Private Sub Window_Loaded() Handles MyBase.Loaded
Dim query = From c In db.Customers.Include("Orders") _
Where c.CustomerID = 1 _
Select c
Me.CustomerData = New CustomerCollection(query, db)
Me.MasterViewSource = CType(Me.FindResource("MasterViewSource"), CollectionViewSource)
Me.DetailViewSource = CType(Me.FindResource("DetailsViewSource"), CollectionViewSource)
Me.MasterViewSource.Source = Me.CustomerData
Me.MasterView = CType(Me.MasterViewSource.View, ListCollectionView)
Me.DetailsView = CType(Me.DetailViewSource.View, BindingListCollectionView)
End Sub
Private Sub MasterView_CurrentChanged() Handles MasterView.CurrentChanged
'We need to grab the new child view when the master's position changes
Me.DetailsView = CType(Me.DetailViewSource.View, BindingListCollectionView)
End Sub
Private Sub btnSave_Click() Handles btnSave.Click
Try
db.SaveChanges()
MessageBox.Show("Customer data saved.", Me.Title, MessageBoxButton.OK, MessageBoxImage.Information)
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Sub
Private Sub btnDelete_Click() Handles btnDelete.Click
If Me.MasterView.CurrentPosition > -1 Then
Me.MasterView.RemoveAt(Me.MasterView.CurrentPosition)
End If
End Sub
Private Sub btnAdd_Click() Handles btnAdd.Click
Dim customer = CType(Me.MasterView.AddNew, Customer)
Me.MasterView.CommitNew()
End Sub
Private Sub btnPrevious_Click() Handles btnPrevious.Click
If Me.MasterView.CurrentPosition > 0 Then
Me.MasterView.MoveCurrentToPrevious()
End If
End Sub
Private Sub btnNext_Click() Handles btnNext.Click
If Me.MasterView.CurrentPosition < Me.MasterView.Count - 1 Then
Me.MasterView.MoveCurrentToNext()
End If
End Sub
Private Sub btnAddDetail_Click() Handles btnAddDetail.Click
Dim order = CType(Me.DetailsView.AddNew, Order)
Me.DetailsView.CommitNew()
End Sub
Private Sub btnDeleteDetail_Click() Handles btnDeleteDetail.Click
If Me.DetailsView.CurrentPosition > -1 Then
Me.DetailsView.RemoveAt(Me.DetailsView.CurrentPosition)
End If
End Sub
Now we can Add, Edit, and Delete Customer and their Orders at the same time and changes will be propagated properly to the database through Entity Framework in one call to SaveChanges. I’ve updated this complete sample application that demonstrates this as well as other aspects of WPF Data Binding with Entity Framework so have a look.
UPDATE: Milind talks about some of the tooling improvements in Visual Studio 2010 on the VSData blog regarding building WPF forms against Entity Data Models so check it out --> WPF Data Binding: Creating a Master-Details form in Visual Studio 2010
Loading Data and Binding Controls in WPF with CollectionViewSource
Published 07 November 08 09:47 AM
When designing WPF Windows with data (or as I usually refer to them WPF "Forms") we have many options on how we want to load the data and bind our controls. Depending on where the data is coming from and how it's being used there are a lot of possibilities.
DataContext "Direct"
Suppose we have the following simple window defined and we've set up data binding on our text boxes to the corresponding properties on a Customer object.
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="152" Width="300" Name="Window1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="95*" />
<ColumnDefinition Width="183*" />
</Grid.ColumnDefinitions>
<StackPanel Name="StackPanel1" Grid.Column="0">
<Label Height="28" Name="Label1" Width="Auto">Customer ID</Label>
<Label Height="28" Name="Label2" Width="Auto">Contact Name</Label>
<Label Height="28" Name="Label3" Width="Auto">Company Name</Label>
</StackPanel>
<StackPanel Name="StackPanel2" Grid.Column="1">
<TextBox Height="28" Name="TextBox1" Width="Auto" Text="{Binding Path=CustomerID}"/>
<TextBox Height="28" Name="TextBox2" Width="Auto" Text="{Binding Path=ContactName}"/>
<TextBox Height="28" Name="TextBox3" Width="Auto" Text="{Binding Path=CompanyName}"/>
</StackPanel>
</Grid>
</Window>
One technique to load the data onto the form is to directly set the DataContext property of a Window or container at runtime and all the contained controls will inherit the same DataContext:
Class Window1
Private Sub Window1_Loaded() Handles MyBase.Loaded
'Returns List(Of Customer) and sets the Window.DataContext
Me.DataContext = CustomerFactory.GetCustomers()
End Sub
End Class
In our simple example above this means that when we run the form any controls on the window will bind to the Customer object. If we set the StackPanel2.DataContext property, then only the controls in the StackPanel will bind to the Customer object. This is a handy way of setting up binding on containers of controls.
Using ObjectDataProvider
Another way to set up binding is to use an ObjectDataProvider in the Window.References section of our XAML which specifies a method to call on a specific type in our project and uses the results as the source of data. Using this technique loads the data at design time as well.
To do this we remove our code in the Loaded event above and instead just specify the XAML to set up the ObjectDataProvider. First we need to add a namespace for our local project and then we can set up the ObjectDataProvider in the Window's resources section. Finally we specify the binding to the ObjectDataProvider by setting the DataContext of the grid container control. All the controls on the form are contained in this grid:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFCollectionViewSource"
Title="Window1" Height="152" Width="300" Name="Window1">
<Window.Resources>
<ObjectDataProvider x:Key="CustomerData"
MethodName="GetCustomers"
ObjectType="{x:Type local:CustomerFactory}"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource CustomerData}}" >
.
.
.
This has the immediate effect of loading the data onto our form in the designer.
Loading the data into the designer can be a good thing if the data source is a local class contained in the project because it gives visual queues as to what the form will actually look like when it runs. However, if you are accessing data in a database, especially if it's not local to your machine, then this can cause potential performance problems at design time. In this case I recommend loading the data at run time only.
Master-Detail Binding
In this simple example we can set the DataContext directly in the Loaded event like we did initially. But things start to get tricky if you have a more complex form. Consider this master/details form that contains a ListView set up in GridView mode specifying DataTemplates for how to bind the columns (similar to how I showed in this video). One of the columns in this GridView is a Lookup list to the employee table (a foreign key relationship to reference data).
<ListView Name="lstDetails" Grid.Row="1" Grid.ColumnSpan="2"
IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn Header="Order Date" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtOrderDate"
Text="{Binding Path=OrderDate}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Ship Date" Width="100" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtShipDate"
Text="{Binding Path=ShippedDate}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Employee" Width="115" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEditable="False"
Name="cboEmployee"
IsSynchronizedWithCurrentItem="False"
SelectedValue="{Binding Path=EmployeeID}"
DisplayMemberPath="LastName"
SelectedValuePath="EmployeeID" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
For a ListView and a Combobox we need to specify the data source of the lists by setting the ItemsSource property. For the ListView it's easy to specify this in the code behind because we have a reference to the lstDetails control. We can also just simply set it here in XAML to the "Orders" collection on the Customer (master). We can set it up this way because my customer objects contain a collection called "Orders" that contain the orders for that customer.
<ListView Name="lstDetails" Grid.Row="1" Grid.ColumnSpan="2"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Orders}">
Note that if we're still using the ObjectDataProvider technique then when specifying this ItemsSource on the lstDetails the designer will populate the GridView with data, which is something we probably don't want to do if the data is stored in a remote database. So we could just set the DataContext in the code behind like in the first example, however, we still need to set up the embedded Combobox in that GridView. Unfortunately it's not exposed as a field of the Window class in our code behind because it's a DataTemplate. There are ways to get at this DataTemplate in code but there's a much easier way to set up our binding using what's called a CollectionViewSource.
Using the CollectionViewSource
A CollectionViewSource is a proxy for the CollectionView which manages the currency (the position) in the list of objects (or rows if using a DataTable). It has a property called Source which can be set in our code behind. This way, we can set up CollectionVieSources in XAML for all our data lists and bind them to the corresponding controls all in XAML. Then at runtime in our code we set the Source properties and only at that time does the data pull from the database.
We need three CollectionViewSource's in our Master/Detail example, one for the Customers (MasterView) one for Orders (DetailView) and one for the list of Employees (EmployeeLookup). On the DetailView we specify the Source as the MasterView with a Path set here to 'Orders'. This is similar to how we chain master/detail BindingSources in Winforms development.
So our XAML will be changed to:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="242" Width="367" Name="Window1">
<Window.Resources>
<CollectionViewSource x:Key="MasterView" />
<CollectionViewSource x:Key="DetailView"
Source="{Binding Source={StaticResource MasterView}, Path='Orders'}" />
<CollectionViewSource x:Key="EmployeeLookup" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource MasterView}}" >
.
.
.
<ListView Name="lstDetails" Grid.Row="1" Grid.ColumnSpan="2"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource DetailView}}">
.
.
.
<ListView.View>
<GridView>
.
.
.
<GridViewColumn Header="Employee" Width="115" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEditable="False"
Name="cboEmployee"
IsSynchronizedWithCurrentItem="False"
SelectedValue="{Binding Path=EmployeeID}"
ItemsSource="{Binding Source={StaticResource EmployeeLookup}}"
DisplayMemberPath="Name"
SelectedValuePath="EmployeeID"
Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
.
.
.
Now we just set the Source properties of our MasterView and EmployeeLookup in our code behind. It's easy to grab references to the CollectionViewSources by accessing the Window.Resources dictionary.
Class Window1
Private Sub Window1_Loaded() Handles MyBase.Loaded
'Returns List(Of Customer) and sets the Master CollectionViewSource,
' the DetailView already has it's source set to the MasterView with
' the child path specified in the XAML.
Dim masterViewSource = CType(Me.Resources("MasterView"), CollectionViewSource)
masterViewSource.Source = CustomerFactory.GetCustomers()
'Returns List(Of Employee and sets the EmployeeLookup CollectionViewSource
Dim employeeViewSource = CType(Me.Resources("EmployeeLookup"), CollectionViewSource)
employeeViewSource.Source = EmployeeFactory.GetEmployees()
End Sub
End Class
This separation also allows us to also easily swap out our data source collections at runtime if necessary.

I show an example of how to use CollectionViewSources and create a fully functional master/detail WPF form in this video and this sample so take a look.
Using the WPF ObservableCollection with EF Entities
Published 08 May 09 03:29 PM
The ObservableCollection is a special WPF collection that provides proper notifications to the UI when items are added, removed, or the list is refreshed because it implements INotifyCollectionChanged. It’s common to use this collection (or inherit from it) to contain your business objects you want to bind to in WPF.
Class Window1
Private CustomerData As ObservableCollection(Of Customer)
You can then set up a CollectionViewSource and use it’s View property to get a reference to the ListCollectionView in order to add and remove items instead of working with the source collection directly. This decouples your data source (and therefore any collection logic) from the form itself making it much easier to change sources later. I’ve showed how to use CollectionViewSources before but basically you just declare them in the Window.Resources section and bind to them in XAML:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="282" Width="440" Name="Window1">
<Window.Resources>
<CollectionViewSource x:Key="CustomerSource" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource CustomerSource}}">
And then you can set the Source property in code to your collection and obtain the ListCollectionView.
Dim customerSource = CType(Me.Resources("CustomerSource"), CollectionViewSource)
customerSource.Source = Me.CustomerData
Me.View = CType(customerSource.View, ListCollectionView)
Then you use the View to add and remove items from the collection and the UI will update properly:
Private Sub btnDelete_Click() Handles btnDelete.Click
If Me.View.CurrentPosition > -1 Then
'removes the currently selected customer from the underlying collection
Me.View.RemoveAt(Me.View.CurrentPosition)
End If
End Sub
Private Sub btnAdd_Click() Handles btnAdd.Click
'adds a new customer to the underlying collection
Dim customer = CType(Me.View.AddNew, Customer)
'do something with customer if needed...
Me.View.CommitNew()
End Sub
Calling these methods on the ListCollectionView will execute the InsertItem and RemoveItem methods on the ObservableCollection.
Now if you are using an Entity Data Model (EDM) the designer in Visual Studio 2008 SP1 will generate entity classes for you that you can also bind to in your UI. Access to these entities is done through the ObjectContext and the designer also creates a class for you that inherits from this when you create the EDM. It is named something like xxxEntites. (For instance, in Visual Studio 2008 SP1 “Add New Item” and select ADO.NET Entity Data Model and name it Northwind.edmx. Generate from Database and select Northwind. Select all the tables and then the designer will generate an ObjectContext called NorthwindEntities and entity classes based on the tables in the database.)
Because the ObjectContext is what tracks changes on entities you can place entities inside an ObservableCollection but in order for the ObjectContext to be notified that adds and deletes need to be tracked you need to write a bit of code. The easiest thing to do is to create your own class that inherits from ObservableCollection and override the InsertItem and RemoveItem methods so that you can tell the ObjectContext to either add or delete the entity which will ultimately execute against the database. In the constructor pass a reference to the ObjectContext. You can also pass in any collection of entities, say from a LINQ query, and then add them to the ObservableCollection. For example:
Imports NorthwindDAL
Imports System.Collections.ObjectModel
Public Class CustomerCollection
Inherits ObservableCollection(Of Customer)
Private _context As NorthwindEntities
Public ReadOnly Property Context() As NorthwindEntities
Get
Return _context
End Get
End Property
Sub New(ByVal customers As IEnumerable(Of Customer), ByVal context As NorthwindEntities)
MyBase.New(customers)
_context = context
End Sub
Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As Customer)
Me.Context.AddToCustomers(item)
MyBase.InsertItem(index, item)
End Sub
Protected Overrides Sub RemoveItem(ByVal index As Integer)
Me.Context.DeleteObject(Me(index))
MyBase.RemoveItem(index)
End Sub
End Class
Then you can use the collection on your WPF form instead like so:
Imports NorthwindDAL
Class Window1
Private db As New NorthwindEntities
Private CustomerData As CustomerCollection
Private View As ListCollectionView
Private Sub Window1_Loaded() Handles MyBase.Loaded
Dim results = From c In db.Customers _
Where c.City.ToLower = "seattle" _
Order By c.LastName, c.FirstName _
Select c
Me.CustomerData = New CustomerCollection(results, db)
Dim customerSource = CType(Me.Resources("CustomerSource"), CollectionViewSource)
customerSource.Source = Me.CustomerData
Me.View = CType(customerSource.View, ListCollectionView)
End Sub
Private Sub btnSave_Click() Handles btnSave.Click
Try
db.SaveChanges()
MsgBox("Customer data was saved.")
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Sub
Private Sub btnDelete_Click() Handles btnDelete.Click
If Me.View.CurrentPosition > -1 Then
Me.View.RemoveAt(Me.View.CurrentPosition)
End If
End Sub
Private Sub btnAdd_Click() Handles btnAdd.Click
Dim customer = CType(Me.View.AddNew, Customer)
'do something with customer if needed...
Me.View.CommitNew()
End Sub
End Class
Now any updates, adds or deletes you make in the UI will be propagated to the database through the Entity Framework.
Data Binding WPF Lookup Combobox Values to EF Entities
Published 30 April 09 07:57 AM
It’s extremely common to have to hook up lookup tables on your data entry forms in order to populate foreign keys in a database. I’ve talked about how to do this in Winforms and WPF with Datasets and LINQ to SQL before:
The common theme between all of these is that the data sources, either the LINQ to SQL classes or the DataTables that we bind to, uses a navigation path based on the foreign key and that foreign key is exposed as a property (or DataColumn). For instance if we have a Customer related to Orders we would have a CustomerID property on Orders.
DataSets (like databases) rely on this type of navigation. So when you want to find the parent Customer of an Order you have to know the relation. Using typed datasets helps you more but you still end up having to know the details of relationships and foreign keys of the DataSet. That’s why people who are familiar with databases are usually comfortable with working with DataSets.

LINQ to SQL classes also include navigation properties as direct object references and collections. So they have both the foreign key and the navigation properties. Customer will have a collection of Orders and Order will have a reference back to Customer but the classes also contain the CustomerID property. This isn’t “pure” I suppose but it does make data binding and subsequent saves back to the database pretty much a no brainer.

For instance say I want to hook up a lookup combobox using a LINQ to SQL class on an Order with a reference to Customer. I want to display a list of Customers the user can pick from and that Customer should be associated with that Order. It’s pretty straight forward and works the same with DataTables.
XAML:
<Window.Resources>
<CollectionViewSource x:Key="OrdersSource" />
<CollectionViewSource x:Key="CustomerLookup" />
</Window.Resources>
<Grid Grid.Row="1" Name="Grid1" DataContext="{Binding Source={StaticResource OrdersSource}}">
<ComboBox Height="23" Name="ComboBox1" Width="177" Margin="2" HorizontalAlignment="Left"
IsEditable="False"
ItemsSource="{Binding Source={StaticResource CustomerLookup}}"
DisplayMemberPath="LastName"
SelectedValuePath="CustomerID"
SelectedValue="{Binding Path=CustomerID}"
/>
...
Code-behind:
Class Window1
Private db As New MyDataObjectContext
Private OrderData As IEnumerable(Of Order)
Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
'Load all the orders from the database
Me.OrderData = db.Orders
'Get the customer lookup list (this is the Combobox ItemsSource)
Dim customerList = From c In db.Customers _
Where c.Orders.Count > 0 _
Order By c.LastName, c.FirstName
Dim ordersSource = CType(Me.FindResource("OrdersSource"), CollectionViewSource)
ordersSource.Source = Me.OrderData
Dim custSource = CType(Me.FindResource("CustomerLookup"), CollectionViewSource)
custSource.Source = customerList.ToList()
End Sub
...
Almost all of this code is just to set up the context of this discussion, you can watch this video for details on building a complete example. Here I’m using CollectionViewSources in the XAML and setting their Source property in code. This technique is handy especially if you are using nested DataTemplates. The important piece to note are the four properties on the Combobox. ItemsSource, DisplayMemberPath, SelectedValue, and SelectedValuePath. To set up your combobox:
- Set the ItemsSource to the list of Customers you want to display in the Combobox.
- Next set the DisplayMemberPath to the property name on this list that you want to use to display in the list, here I used LastName.
- Then set the SelectedValuePath to the property name on this list that will be used to populate the foreign key value on the Order.
- Finally you set the SelectedValue to the property binding on the Order that is foreign key. LINQ to SQL (and DataSets) will happily save your data with this binding in place.
This is how you always bind DataTables and it also works well for LINQ to SQL classes. But since LINQ to SQL classes also include the navigation properties (the Orders collection on Customer and the Customer object reference on Order) you can use a different technique by binding directly to the Customer reference. This is the only choice we have with Entity Framework entities in .NET 3.5 SP1.
What’s unique with Entity Framework is that the associations between other entities use only navigation properties -- so there isn’t a CustomerID foreign key property value on the Order at all.

What you have to do instead is bind directly to the Customer object reference. The change in the above example is the Combobox binding in XAML:
<ComboBox Height="23" Name="ComboBox1" Width="177" Margin="2" HorizontalAlignment="Left"
IsEditable="False"
ItemsSource="{Binding Source={StaticResource CustomerLookup}}"
DisplayMemberPath="LastName"
SelectedItem="{Binding Path=Customer}"
/>
The important pieces in this case are the three properties on the Combobox. ItemsSource, DisplayMemberPath, and SelectedItem.
- Set the ItemsSource to the list of Customers you want to display in the Combobox -- same as before.
- Next set the DisplayMemberPath to the property name on this list that you want to use to display in the list, here I used LastName – same as before.
- Now set the SelectedItem to the property binding on the Order that is the navigation property to Customer.
This will work properly with LINQ to SQL classes and Entity Framework entities but there is one caveat. You need to make sure that you pull the entire Customer entity into in the lookup list from the same ObjectContext you used to query the Orders. This is because the same ObjectContext (DataContext in LINQ to SQL) needs to resolve the entity references between the queries. Entity Framework does this based on the EntityKeys. The neat side effect of this with EF is that you don’t have to pull down the Customers with the Orders query, they will automatically become references when the Customer lookup list is queried through the same context. (See my post on explicit load for more details on how to bring down related EF entities when you only make one call.)
This behavior may be desired in a lot of scenarios but if we do not need to modify the Customer, like in our example, this can be overkill especially if the Customer has a lot of large fields you aren’t using. In the first example we could have only pulled a subset of fields from the Customer table – the only required ones would be the ones used in the data binding, CustomerID and LastName. So with DataTables and LINQ to SQL classes that bind on the values we could have optimized our lookup list query to:
Dim customerList = From c In db.Customers _
Where c.Orders.Count > 0 _
Order By c.LastName, c.FirstName _
Select c.CustomerID, c.LastName
This will not work if we bind directly to the navigation properties because the customerList is now a list of anonymous types and not a list of Customer entities. So binding to the values gives you greater flexibility with your lookup list queries. Unfortunately in the current version of EF you cannot bind this way but they are planning to enable this in the next version. In .NET 4.0 the EF team will add support for a new type of association called "FK Associations". Read more about that here.
And I’m working on the next set of WPF Forms over Data How Do I videos, this time with Entity Framework, that will hopefully explain how to use EF in practical way by building WPF data applications. Stay tuned!
Notifying the UI when Entity References Change in Lookup Comboboxes
Published 04 May 09 07:51 PM
Last week I wrote about how to data bind WPF lookup comboboxes to entities returned from the Entity Framework. I described that the key to this type of binding is setting the SelectedItem to the object reference itself on the navigation property instead of setting SelectedValue and SelectedValuePath as in the case when you have foreign key scalar properties like LINQ to SQL classes or DataTables.
However, depending on your UI, you may need a notification to fire when the entity reference changes. By default this doesn’t happen with entities generated by the EF designer. Only scalar properties raise change notifications. For instance, going back to our Customer (1)—(*) Order example, the Order entity has a reference to its Customer parent as specified by the navigation property:

In the database there is a foreign key relationship on CustomerID and that is inferred here by EF. If you look at the Order class that is generated you will see only change notifications raised on the scalar properties, not the navigation properties. For instance, if we take a look at a scalar property that is generated you will see the change notification partial methods generated as well:
Partial Public Class Order
Inherits Global.System.Data.Objects.DataClasses.EntityObject
.
.
.
Public Property OrderID() As Integer
Get
Return Me._OrderID
End Get
Set
Me.OnOrderIDChanging(value)
Me.ReportPropertyChanging("OrderID")
Me._OrderID = StructuralObject.SetValidValue(value)
Me.ReportPropertyChanged("OrderID")
Me.OnOrderIDChanged
End Set
End Property
Private _OrderID As Integer
Partial Private Sub OnOrderIDChanging(ByVal value As Integer)
End Sub
Partial Private Sub OnOrderIDChanged()
End Sub
.
.
.
EF entities that are generated by the designer inherit from EntityObject that in turn inherits from StructuralObject that implements INotifyPropertyChanged. This interface is necessary for notifying the UI (WPF and Winforms) that data bound controls should refresh their value. So say you programmatically change a scalar property then any controls bound to that property will be refreshed with the new value automatically. Or in many cases you have a UI with multiple controls bound to the same property. If the user makes a change to one control, the rest update automatically.
However this notification isn’t generated on entity references. Which means that if you have a lookup combobox set up like I described in last week’s post and also have another control bound to the same Customer navigation property, then it won’t refresh properly.
For instance, say we have an Order form with a combobox set up like before, where the SelectedItem is bound to the Customer property (SelectedItem="{Binding Path=Customer}"), but we also have a listbox that shows OrderDate, Customer.LastName, Customer.FirstName:

<ListBox Grid.Row="1" Name="ListBox1" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="60" Text="{Binding Path=OrderDate, StringFormat='d'}" />
<TextBlock Text="{Binding Path=Customer.LastName}" />
<TextBlock Width="5">, </TextBlock>
<TextBlock Text="{Binding Path=Customer.FirstName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
If the user changes the OrderDate then that change will automatically be reflected in the listbox. But if the user changes the Customer in the dropdown combobox then it will NOT update the listbox because a change notification is not raised on Customer. What’s also interesting is if you look at that part of the generated Order entity then you will actually see two properties, one we expect called Customer and one called CustomerReference:
.
.
.
Public Property Customer() As Customer
Get
Return CType(Me, IEntityWithRelationships).RelationshipManager. _
GetRelatedReference(Of Customer)("OMSModel.FK_Orders_Customer", "Customer").Value
End Get
Set(ByVal value As Customer)
CType(Me, IEntityWithRelationships).RelationshipManager. _
GetRelatedReference(Of Customer)("OMSModel.FK_Orders_Customer", "Customer").Value = value
End Set
End Property
.
.
.
Public Property CustomerReference() As EntityReference(Of Customer)
Get
Return CType(Me, IEntityWithRelationships).RelationshipManager. _
GetRelatedReference(Of Customer)("OMSModel.FK_Orders_Customer", "Customer")
End Get
Set(ByVal value As EntityReference(Of Customer))
If (Not (value) Is Nothing) Then
CType(Me, IEntityWithRelationships).RelationshipManager. _
InitializeRelatedReference(Of Customer)("OMSModel.FK_Orders_Customer", "Customer", value)
End If
End Set
End Property
The Customer property is a navigation property to the parent Customer entity itself as we expect. The CustomerReference is an EntityReference class. This class describes the relationship between the Order and Customer. It also defines an event called AssociationChanged that you can handle to notify the UI properly when the reference changes. When you change the reference this event will fire twice, first to remove the old reference and then again to add the new one. You can easily extend the Order partial class by creating another Partial Class declaration for Order in the same namespace (which is automatically imported in VB) and then calling the appropriate property change notifications:
Imports System.ComponentModel
Partial Public Class Order
Sub New()
MyBase.New()
AddHandler Me.CustomerReference.AssociationChanged, AddressOf Customer_AssociationChanged
End Sub
Private Sub Customer_AssociationChanged(ByVal sender As Object, _
ByVal e As CollectionChangeEventArgs)
If e.Action = CollectionChangeAction.Remove Then
OnPropertyChanging("Customer")
Else
OnPropertyChanged("Customer")
End If
End Sub
End Class
So now we can change the Customer in the dropdown and the UI will be notified properly. Sweet. For more information on Entity Framework and data binding see this topic in the MSDN library.
Implementing Validation in WPF on Entity Framework Entities
Published 07 July 09 05:14 PM
I’ve blogged before about implementing validation on LINQ to SQL classes as well as how to customize the display of error messages in WPF. In this post I want to show how you can use these same techniques to validate entities coming from the Entity Framework (EF). Like LINQ to SQL classes, Entity Framework entities are implemented as partial classes so that you can extend them with your own code on top of the code that the designers generate for you. You can extend EF entities in a similar way as LINQ to SQL classes.
Creating the Partial Class
Let’s take the example that I started here in this How Do I video on building a simple data entry form to edit customers. You can download the code for that video here. In this sample I have two projects, one for the WPF client (WpfEfDataEntry) and one for the Data Access Layer (WpfEfDAL) that contains a simple Entity Data Model (edmx) of a little database I created that tracks customers and their orders.


To extend the Customer class that is generated from the EF designer, right-click on the DAL project and select Add –> Class then name it Customer. This places the class in the same Namespace as the entities that are generated by the designer. This is necessary for partial classes to work. (Partial classes are just a way that you can define one class in multiple physical files and Visual Studio will handle compiling them into one class for you.)
Here’s a trick in VB. You know you got your partial class in the right namespace when you drop down the Declarations dropdown and you see the list of partial methods and properties that the class defines.
Also in VB the Partial keyword is only required on one of the class declarations in one of the files. (In C# it’s required on all of them.) The EF designer generates the Customer class with the partial keyword. If you click the “Show all Files” button on the Solution Explorer toolbar and then expand the .edmx you can open the .Designer file and see the entity Partial Class definitions:
Partial Public Class Customer
You can of course be explicit in VB and add the Partial keyword to all your partial class files as well.
Adding Validation to the Partial Class
To add validation we can implement the IDataErrorInfo interface in our customer partial class. Using this interface will make validation errors display in Winforms as well as WPF so I tend to prefer this implementation over others like ValidationRules collection in WPF. For this example let’s make sure that the LastName field isn’t empty but we’ll also provide a default value by specifying it in the constructor. This code is the same code we would use if we were working with LINQ to SQL classes.
Imports System.ComponentModel
Partial Public Class Customer
Implements IDataErrorInfo
#Region "IDataErrorInfo Members"
Private m_validationErrors As New Dictionary(Of String, String)
Private Sub AddError(ByVal columnName As String, ByVal msg As String)
If Not m_validationErrors.ContainsKey(columnName) Then
m_validationErrors.Add(columnName, msg)
End If
End Sub
Private Sub RemoveError(ByVal columnName As String)
If m_validationErrors.ContainsKey(columnName) Then
m_validationErrors.Remove(columnName)
End If
End Sub
Public ReadOnly Property HasErrors() As Boolean
Get
Return m_validationErrors.Count > 0
End Get
End Property
Public ReadOnly Property [Error]() As String _
Implements System.ComponentModel.IDataErrorInfo.Error
Get
If m_validationErrors.Count > 0 Then
Return "Customer data is invalid"
Else
Return Nothing
End If
End Get
End Property
Default Public ReadOnly Property Item(ByVal columnName As String) As String _
Implements System.ComponentModel.IDataErrorInfo.Item
Get
If m_validationErrors.ContainsKey(columnName) Then
Return m_validationErrors(columnName).ToString
Else
Return Nothing
End If
End Get
End Property
#End Region
Public Sub New()
'Set defaults
Me.LastName = "[new]"
End Sub
Now we can write our validation code to check the LastName field. If you look back at the generated Customer class in the .Designer file, notice that there are OnFieldNameChanging and OnFieldNameChanged methods that are also declared as Partial. These are partial methods, a new feature introduced with Visual Studio 2008, that allow you to supply additional code that is called from the generated class. The Changing/Changed methods are called in the property setters. We’ll define the OnLastNameChanged to make sure the user enters a LastName:
''' <summary>
''' This method is called in the LastName property setter of the customer
''' partial class generated by the Entity Data Model designer.
''' </summary>
Private Sub OnLastNameChanged()
'Perform validation.
If _LastName Is Nothing OrElse _LastName.Trim() = "" OrElse _LastName.Trim() = "[new]" Then
Me.AddError("LastName", "Please enter a last name.")
Else
Me.RemoveError("LastName")
End If
End Sub
End Class
Now all we need to do is specify on the binding in the XAML of the WPF form to display the validation errors.
<TextBox Name="txtLastName" Width="Auto" Height="28" Margin="3"
Text="{Binding Path=LastName, ValidatesOnDataErrors=True}"/>
This is exactly the same as we did in this post when working with LINQ to SQL. Read that post to also see how to change the default error template which controls how the errors are displayed.
Adding Validation to Entity References
As you can see validating scalar properties on EF entities works the same as with LINQ to SQL classes. However what if we wanted to make sure that an entity reference was also specified on an EF entity? I’ve posted before about how to get notified when entity references change. But what if we also want to make sure an entity reference is not empty? For instance, in the case of the Order entity above, how would we write a validation to make sure that the Customer entity reference was specified on the Order before we tried to save?
Looking back up at the Customer (1)—(*) Order in the diagram above, the Order entity has a reference to its Customer parent as specified by the navigation property. In the database there is a foreign key relationship on CustomerID and that is inferred here by EF. This is a difference from LINQ to SQL classes where the classes contain the foreign keys as scalar properties as well. We can’t validate EF entities the same way because there are no scalar properties for the foreign keys. Instead we need to add an event handler to the AssociationChanged event on the entity reference (like I showed before) and then add in our validation. Remember that the AssociationChanged event will fire twice when we are selecting a new reference, once when the old entity reference is removed and then once when the new one is added.
Imports System.ComponentModel
Public Class Order
Implements IDataErrorInfo
Public Sub New()
'Handle this event so that UI can be notified if the customer is changed
AddHandler Me.CustomerReference.AssociationChanged, AddressOf Customer_AssociationChanged
'Set defaults
Me.OrderDate = Date.Today()
'Customer is required
Me.AddError("Customer", "Please select a customer.")
End Sub
Private Sub Customer_AssociationChanged(ByVal sender As Object, _
ByVal e As CollectionChangeEventArgs)
If e.Action = CollectionChangeAction.Remove Then
OnPropertyChanging("Customer")
Else
If e.Action = CollectionChangeAction.Add Then
Me.RemoveError("Customer")
End If
OnPropertyChanged("Customer")
End If
End Sub
What I’m doing is putting the Order in an immediate error state so that the user can see that a Customer must be selected on the Order before it is valid. The error will only go away once they select a Customer.
I’ve created a sample application that you can download from Code Gallery that demonstrates using EF with WPF in a variety of ways including this example so have a look. Also make sure you check out these How Do I videos on EF and WPF.
Enjoy!
Posted
2009/8/27 14:11
by
革命