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:

image

<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.

Enjoy!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Notifying the UI when Entity References Change in Lookup Comboboxes | Microsoft Share Point said on May 4, 2009 11:11 PM:

PingBack from http://microsoft-sharepoint.simplynetdev.com/notifying-the-ui-when-entity-references-change-in-lookup-comboboxes/

# Daniele Armanasco said on May 10, 2009 2:05 AM:

I have the same problem but I'm using Linq2Sql; is there anything similar to AssociationChanged in Linq2Sql?

Thank you.

# Daniele Armanasco said on May 10, 2009 10:34 AM:

I have the same problem using Linq2Sql; is there anything similar to AssociationChanged in Linq2Sql?

Thank you.

# Beth Massi said on May 11, 2009 11:13 AM:

Hi Daniele,

In L2S you can use the technique as when working with DataTables (http://msdn.microsoft.com/en-us/vbasic/cc788742.aspx), bind to the foreign key values instead of the association.

<ComboBox IsEditable="False"

     DisplayMemberPath="LastName"

     SelectedValuePath="CustomerID"

     SelectedValue="{Binding Path=CustomerID}" />

HTH,

-B

# Daniele Armanasco said on May 12, 2009 10:45 AM:

Thank you Beth.

To be honest my problem is different; I was simplyfing it because AssociationChanged could solve my trouble. In the truth I need to recalculate some temporary field when I add/remove an associated object. I tried these:

- call my method in the setter of the association (in the designer); it works but every time I edit the db schema the designer erase my code;

- implements OnIdmyobjectChanged() in the partial class but it isn't raised when I add/remove myobject to/from the association.

Am I missing something?

# Visual Studio Data said on May 15, 2009 10:06 PM:

I’ve been writing a lot about building WPF business applications with Entity Framework using Visual Studio

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About Beth Massi

Beth is a Program Manager on the Visual Studio Community Team at Microsoft and is responsible for producing and managing content for business application developers, driving community features and team participation onto MSDN Developer Centers (http://msdn.com), and helping make Visual Studio one of the best developer tools in the world. She also produces regular content on her blog (http://blogs.msdn.com/bethmassi), Channel 9, and a variety of other developer sites and magazines. As a community champion and a long-time member of the Microsoft developer community she also helps with the San Francisco East Bay .NET user group and is a frequent speaker at various software development events. Before Microsoft, she was a Senior Architect at a health care software product company and a Microsoft Solutions Architect MVP. Over the last decade she has worked on distributed applications and frameworks, web and Windows-based applications using Microsoft development tools in a variety of businesses. She loves teaching, hiking, mountain biking, and driving really fast.

This Blog

Syndication

Page view tracker