When building a complex data visualization application in Silverlight, I discovered a disturbing issue when dealing with multiple DataGrids on the same screen. If the ItemsSource of a particular DataGrid gets updated as the result of a SelectedItem change in another DataGrid the child grid would not display any items. Worse, adding code to query the ItemsSource of the child grid shows that does have a valid and correct ItemsSource, so why doesn’t it display? In this article we’ll quickly demonstrate the problem and show a solution.
Sample App
I’ve created a simple example with a ‘parent’ DataGrid containing Orders and a ‘child’ DataGrid meant to display the Order Details of the selected Order. Throw in a small amount of sample data and we wind up with this:
You can see that we have selected the first and only row in the top DataGrid and it has a valid OrderDetails property. Clicking on the Check Items Source button reveals that something should be shown in the bottom DataGrid:
So what’s going on? Let’s take a look at the XAML and data binding code to see if anything is amiss.
Data Binding Code
I have created a simple ViewModel for this screen, with code as follows:
public class ScreenModel : BindableType
{
public ScreenModel()
{
Orders = new ObservableCollection<Order>();
Orders.Add(new Order());
}
private ObservableCollection<Order> _Orders;
public ObservableCollection<Order> Orders
{
get { return _Orders; }
set
{
_Orders = value;
OnPropertyChanged("Orders");
}
}
private Order _SelectedOrder;
public Order SelectedOrder
{
get { return _SelectedOrder; }
set
{
_SelectedOrder = value;
//Doesn't work:
OnPropertyChanged("SelectedOrder");
BindableType implements the basic INotifyPropertyChanged functionality. Hopefully you agree that this is extremely vanilla code, and that it’s a bit odd that this does not work. Maybe something is wrong with the XAML?
<TextBlock>Master Grid</TextBlock>
<my:DataGrid x:Name="MasterGrid" MinHeight="100"
ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder, Mode=TwoWay}" />
<TextBlock>Detail Grid</TextBlock>
<my:DataGrid x:Name="DetailGrid" MinHeight="100" ItemsSource="{Binding SelectedOrder.OrderDetails}" />
<Button x:Name="ChkBtn" Content="Check Items Source" Click="ChkBtn_Click"></Button>
The XAML is extremely basic as well. How can you fix this? Searching through forums it seems that when Silverlight 3 was in BETA changing the bottom DataGrid to have a TwoWay binding to its ItemsSource would fix this but I didn’t have luck with this.
Problem Solution
By groping in the dark and changing a single line of code I was able to get the correct behavior:
The only code change is this:
private Order _SelectedOrder;
public Order SelectedOrder
{
get { return _SelectedOrder; }
set
{
_SelectedOrder = value;
//Doesn't work:
//OnPropertyChanged("SelectedOrder");
//Works:
Deployment.Current.Dispatcher.BeginInvoke(() => OnPropertyChanged("SelectedOrder"));
}
}
All I’ve done here is to invoke the Property Change Notification for SelectedOrder back onto a Dispatcher thread. Why is this necessary? I can’t say for sure. My guess is that something down in the bowels of Silverlight is attempting to detect some sort of layout cycle or infinite recursion here. If you find this situating in your code this should work for you.