views:

3158

answers:

2

I'm trying to get a WPF combobox working (inside the WPFToolkit Datagrid), and I'm having trouble getting all the pieces aligned correctly. I'm using Linq to Entities, and I'm setting the overall datacontext 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 };
}

I also have a page property in my code-behind which provides the ItemsSource for the combobox:


private IEnumerable donationPurposeList;
public IEnumerable DonationPurposeList
{
    get
    {
     if (donationPurposeList == null)
     {
      donationPurposeList = from dp in entities.DonationPurpose
             select dp;
     }
     return donationPurposeList.ToList();
    }
}

The XAML for the combobox looks like this:

<tk:DataGridTemplateColumn Header="Purpose">
    <tk:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=donations.DonationPurpose.Description, Mode=TwoWay}" />
        </DataTemplate>
    </tk:DataGridTemplateColumn.CellTemplate>
    <tk:DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox Name="cboDonationPurpose"
                SelectedValue="{Binding Path=donations.DonationPurposeID, Mode=TwoWay}"
                ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Page},Mode=FindAncestor},Path=DonationPurposeList}"                                 
                DisplayMemberPath="Description"
                SelectedValuePath="DonationPurposeID"
                      />
        </DataTemplate>
    </tk:DataGridTemplateColumn.CellEditingTemplate>
</tk:DataGridTemplateColumn>

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 SelectedItem instead of SelectedValue, but that ends up with the selected value not showing in the ComboBox. I'm kinda mystified: it seems like this bit should be working.

Edited 3/2/09: I'm still puzzling over this. I should note that in my datagrid, any simple data columns (e.g., "DataGridTextColumn") update the underlying data source just as you'd expect. But any update to any of my templated columns ("DataGridTemplateColumn") or ComboBox columns ("DataGridComboBoxColumn") don't work: the underlying data source never gets updated. Surely other folks have tried to use the WPF.Toolkit datagrid and gotten this scenario to work -- but I've looked at all the sample code out there, and I'm doing basically what it says to do (within the constraints of my solution), so I'm scratching my head why this isn't working.

Any thoughts?

A: 

http://www.codeplex.com/wpf/Thread/View.aspx?ThreadId=39696

Check out this post and see if it helps.

+3  A: 

I was able to get this working. But I set things up a wee bit differently.

  1. I created a ViewModel to act as a contract with the View.
  2. I bound to the ComboBox.SelectedItem Property instead of ComboBox.SelectedValue Property

Since I didn't know what your data source was I made up my own to simulate the basic problem: having a comboBox bind correctly within a WPF DataGrid.

Here is the composition of my View Model:

public class RootViewModel
{
    public List<State> USStates { get; set; }
    public List<Customer> Customers { get; set; }

    public ViewModel()
    {
        Customers = new List<Customer>();
        Customers.Add(new Customer() { FirstName = "John", LastName = "Smith", State = new State() { ShortName = "IL" } });
        Customers.Add(new Customer() { FirstName = "John", LastName = "Doe", State = new State() { ShortName = "OH" } });
        Customers.Add(new Customer() { FirstName = "Sally", LastName = "Smith", State = new State() { ShortName = "IN" } });

        USStates = new List<State>();
        USStates.Add(new State() { ShortName = "OH" });
        USStates.Add(new State() { ShortName = "IL" });
        USStates.Add(new State() { ShortName = "IN" });
    }
}

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public State State { get; set; }
}

public class State
{
    public string ShortName { get; set; }

    public State() 
    {
        ShortName = string.Empty;
    }

    public override bool Equals(object obj)
    {
        if (obj is State)
        {
            State otherState = obj as State;
            return ShortName.Equals(otherState.ShortName);
        }
        else
        {
            return false;
        }
    }
}

Before we begin, I set the DataContext of my Window to be an instance of my properly constructed RootViewModel.

<tk:DataGrid ItemsSource="{Binding Customers}" AutoGenerateColumns="False">
            <tk:DataGrid.Columns>
                <tk:DataGridTemplateColumn Header="State">
                    <tk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=State.ShortName, Mode=TwoWay}" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellTemplate>
                    <tk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox 
                                x:Name="cboDonationPurpose" 
                                SelectedItem="{Binding Path=State, Mode=TwoWay}" 
                                ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window},Mode=FindAncestor}, Path=DataContext.USStates}" 
                                DisplayMemberPath="ShortName" 
                                SelectedValuePath="ShortName" />
                        </DataTemplate>
                    </tk:DataGridTemplateColumn.CellEditingTemplate>
                </tk:DataGridTemplateColumn>
            </tk:DataGrid.Columns>
        </tk:DataGrid>

In order for the SelectedItem to bind properly I need to ensure that I have overriden the Equals method on my entity since under the hood, WPF is using this method to determine who is the SelectedItem or not. I think this was fundamentally your problem from the beginning which caused you to try to bind to SelectedValue instead of SelectedItem.

markti