DataGridComboBoxColumn not updating underlying data source

Feb 26, 2009 at 10:09 PM
I'm obviously doing something wrong, but I can't quite figure out what it is.  It seems like this should be a fairly basic scenario.  I'm using Linq to Entities, and I'm setting the overall datacontext of the datagrid to the results of a Linq query:

private void LoadDonationGrid()
{
    donationGrid
.ItemsSource = from donations in entities.Donation
                           
.Include("Family")
                           
.Include("PledgeYear")
                           
.Include("DonationPurpose")
                   
from donationPurposes in entities.DonationPurpose
                   
select new { donations, donationPurposes };
}
For the ItemsSource property of the DataGridComboBoxColumn, I'm setting an ObjectDataProvider with the results of another Linq query:

// Set the XAML's object data provider resource.
ObjectDataProvider odp = (ObjectDataProvider)FindResource("odpDonationPurposeList");
odp
.ObjectInstance = from dp in entities.DonationPurpose select dp;

The XAML in question looks like this, i.e., I'm setting the ItemsSource for the combobox to the "odpDonationPurposeList" ObjectDataProvider, I'm telling it to display the Description property of the DonationPurpose class, and I'm binding the result to the DonationPurpose property of the Donation class.

<tk:DataGridComboBoxColumn Header="Purpose (native)" 
     
ItemsSource="{Binding Source={StaticResource odpDonationPurposeList}}"
     
SelectedItemBinding="{Binding Path=donations.DonationPurpose}"
     
DisplayMemberPath="Description"
                           
/>

And everything seems to work correctly, i.e., the appropriate values are displayed in the ComboBox, right up to the point where focus leaves the ComboBox. At that point, the displayed value returns to the original value, not to the newly selected value. I've tried using SelectedValue and SelectedValuePath instead of SelectedItem, but the end result is the same.

Thoughts on what I'm doing wrong?  I've been trying to get this stupid scenario working for the better part of a full day, and it's getting frustrating.
Coordinator
Mar 2, 2009 at 6:32 PM
<tk:DataGridComboBoxColumn Header="Purpose (native)"     
                                                   ItemsSource="{Binding Source={StaticResource odpDonationPurposeList}}"      
                                                   SelectedItemBinding="{Binding Path=donations.DonationPurpose}"     
                                                   DisplayMemberPath="Description"/>

I dont think you need the stuff in bold ("donations."). Try the following instead....

<tk:DataGridComboBoxColumn Header="Purpose (native)"     
                                                   ItemsSource="{Binding Source={StaticResource odpDonationPurposeList}}"     
                                                   SelectedItemBinding="{Binding Path=DonationPurpose}"    
                                                   DisplayMemberPath="Description"/>
Mar 2, 2009 at 10:29 PM
Thanks for the response.  I still haven't been able to get it to work, however.  I've figured out a few things since I last posted (I'm still new to LINQ, WPF, etc.), and I've simplified my code just a bit -- but I've tried your suggestion, and a variety of other things, and it's still not working.  

Here's my current XAML:

<Page x:Class="WouldBeTheologian.PledgeManager.Pages.ManageDonations"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
    Title="ManageDonations" Loaded="Page_Loaded">
<Page.Resources>
<ObjectDataProvider x:Key="DonationPurposeList"/>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel>
<TextBlock>Manage Donations</TextBlock>
</StackPanel>
<tk:DataGrid Grid.Row="1" ItemsSource="{Binding}" Name="donationGrid" AutoGenerateColumns="False" SelectionChanged="donationGrid_SelectionChanged">
<tk:DataGrid.Columns>
<tk:DataGridTemplateColumn Header="Family">
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Family.PrimaryLastName}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Family.Tag}" />
</StackPanel>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridTemplateColumn Header="Received">
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<tk:DatePicker SelectedDate="{Binding Path=ReceivedOn, Mode=TwoWay}" BorderThickness="0"/>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridComboBoxColumn Header="Purpose" 
                                           ItemsSource="{Binding Source={StaticResource DonationPurposeList}}" 
                                           SelectedItemBinding="{Binding Path=DonationPurpose}"
                                           DisplayMemberPath="Description"
                                           />
<tk:DataGridTextColumn Header="Pledge Year" Binding="{Binding PledgeYearID}" />
<tk:DataGridTextColumn Header="Amount" Binding="{Binding Path=Amount, StringFormat=C}" />
</tk:DataGrid.Columns>
</tk:DataGrid>
</Grid>
</Page>


Here's the relevant bits of the code-behind:

public partial class ManageDonations : Page
{
private PledgesEntities entities = new PledgesEntities();

public ManageDonations()
{
InitializeComponent();
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
LoadDonationGrid();
LoadDonationPurposeData();
}

private void LoadDonationGrid()
{
donationGrid.ItemsSource = from donation in entities.Donation
  .Include("Family")
  select donation;
}

private void LoadDonationPurposeData()
{
// Set the XAML's object data provider resource.
ObjectDataProvider odp = (ObjectDataProvider)FindResource("DonationPurposeList");
odp.ObjectInstance = from dp in entities.DonationPurpose select dp;
}

private void donationGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
entities.SaveChanges();
}
catch (System.Data.UpdateException ex)
{
MessageBox.Show("There was an error updating the database: " + ex.InnerException.Message);
}
catch (System.Exception ex)
{
Utils.HandleGenericError(ex);
}
}
}

The current behavior of the grid is that if I update any of the textbox columns (say, PledgeYear or Amount), the underlying dataset gets updated, and flushed successfully to the DB.  But if I update either the DatePicker column ("ReceivedOn") or the ComboBox column ("DonationPurpose"), the underlying entity data model is not updated, and nothing is flushed to the DB.  More-or-less the same code works if I try it outside of a DataGrid, so I'm left thinking that it must have something to do with how the DataGrid updates (or doesn't update) its data bindings.

Any other thoughts?
Mar 3, 2009 at 6:43 AM
One thing I would definitely try is to put a breakpoint at the entry point to the setter for the business object property that does not seem to get updated. Verify that it is actually being called, and double-check that "value" has the value that you expect.

You could also try removing the exception handling in donationGrid_SelectionChanged() so that any exception that gets thrown by entities.SaveChanges() is immediately caught by the debugger instead of being passed to another function.