I'm using EntityFramework, WPF and MVVM in my application and got some problems with updating the databinding of relationships between EntityObjects. I was able to downsize my problem to only a few lines of XAML and I hope someone can help me as I'm still not very confident with EF and MVVM.
Anyway, here we go with the simplified XAML:
<DatePicker Grid.Row="2" Grid.Column="1"
SelectedDate="{Binding Path=File.SentDate,
StringFormat={}{0:dd/MM/yyyy}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center" IsEnabled="{Binding Path=IsEnabled}"/>
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding Contacts}" DisplayMemberPath="Name"
SelectedItem="{Binding Path=File.Sender, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"
VerticalAlignment="Center">
</ComboBox>
<Label Content="{Binding Path=File.SenderId}" Grid.Row="4"/>
<Label Content="{Binding Path=File.Sender.Name}" Grid.Row="5"/>
<Label Content="{Binding Path=File.SentDate}" Grid.Row="6"/>
I'm using the last 3 Labels to test my databinding. Changing the File.SentDate using the DatePicker updates the databinding to the last Label without problem.
Now File is of type EntityObject and has a SenderId property of type GUID. It also has a relationship to my Contacts through the Sender property. Obvisouly, SenderId is the GUID of the corresponding Contact EntityObject which is related to File through the Sender relationship. A File can have only 1 single Sender of type Contact.
Anyway, what happens is that when I select another sender using the combobox, the Label displaying the File.SenderId property get properly updated. However, the one with the File.Sender.Name property i.e. the one using the reléationship does not get updated.
So I'm guessing that there is something special about updating the databinding of relationships in EF.
Can someone please suggest a solution to this?
Unfortunately, the Entity Framework doesn’t notify when an association property changes. That’s the reason why your Binding didn’t work.
The issue is reported to Microsoft: http://connect.microsoft.com/VisualStudio/feedback/details/532257/entity-framework-navigation-properties-don-t-raise-the-propertychanged-event
Another workaround is shown by the BookLibrary sample application of the WPF Application Framework (WAF). The Book class listens to the AssociationChanged event and raises the appropriate PropertyChanged event.
public Book()
{
…
LendToReference.AssociationChanged += LendToReferenceAssociationChanged;
}
private void LendToReferenceAssociationChanged(object sender,
CollectionChangeEventArgs e)
{
// The navigation property LendTo doesn't support the PropertyChanged event.
// We have to raise it ourselves.
OnPropertyChanged("LendTo");
}
Looks like I've found a solution, though to me its more like a workaround. It's not the solution I
would have expected but it works.
The XAML is still the same as above, except for one thing. Instead of binding to File.Sender.Name, I bind to File.SenderName like this:
<Label Content="{Binding Path=File.SenderName}" Grid.Row="4"/>
SenderName in this case is a property of the object File which I added in a partial class like this:
public partial class File
{
public string SenderName
{
get
{
if (this.Sender != null)
{
return this.Sender.Name;
}
return string.Empty;
}
}
protected override void OnPropertyChanged(string property)
{
if (property == "SenderId")
{
OnPropertyChanged("SenderName");
}
base.OnPropertyChanged(property);
}
}
So what happens here is that if the SenderId property is changed, I tell the framework to also update the SenderName property. That's it. Works like a charm. Although I'm still not convinced that this is the way it is supposed to work.
Another workaround if you simply want a name is to overide ToString() for the Sender and bind directly to sender. This workaround is good because most of the time when we are databinding to Property of a Property we do it in order to get a "name" of object set as property value. Also this method works for Database First approach too if you edit tt files to add partial to all class definitions.
So you add a file to contain ToString extensions of your Entites and in it you add something like this:
public partial Contacts
{
public override string ToString()
{
return Name;
}
}
so you can databind
<Label Content="{Binding Path=File.Sender}" Grid.Row="5"/>
Now the databinding will detect if the Sender changes, and when it does it will call ToString to determine what to display.
On the other hand if you need to bind to another non standard property you might have problems. I do remember having success with using DataContext and templates to get around it. You bind to Sender and use DataTemplate to determine what to display.