Binding ItemsSource of a ComboBoxColumn in WPF Dat

2019-01-01 05:49发布

I have two simple Model classes and a ViewModel...

public class GridItem
{
    public string Name { get; set; }
    public int CompanyID { get; set; }
}

public class CompanyItem
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class ViewModel
{
    public ViewModel()
    {
        GridItems = new ObservableCollection<GridItem>() {
            new GridItem() { Name = "Jim", CompanyID = 1 } };

        CompanyItems = new ObservableCollection<CompanyItem>() {
            new CompanyItem() { ID = 1, Name = "Company 1" },
            new CompanyItem() { ID = 2, Name = "Company 2" } };
    }

    public ObservableCollection<GridItem> GridItems { get; set; }
    public ObservableCollection<CompanyItem> CompanyItems { get; set; }
}

...and a simple Window:

<Window x:Class="DataGridComboBoxColumnApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name}" />
                <DataGridComboBoxColumn ItemsSource="{Binding CompanyItems}"
                                    DisplayMemberPath="Name"
                                    SelectedValuePath="ID"
                                    SelectedValueBinding="{Binding CompanyID}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

The ViewModel is set to the MainWindow's DataContext in App.xaml.cs:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        MainWindow window = new MainWindow();
        ViewModel viewModel = new ViewModel();

        window.DataContext = viewModel;
        window.Show();
    }
}

As you can see I set the ItemsSource of the DataGrid to the GridItems collection of the ViewModel. This part works, the single Grid line with Name "Jim" is displayed.

I also want to set the ItemsSource of the ComboBox in every row to the CompanyItems collection of the ViewModel. This part does not work: The ComboBox remains empty and in the Debugger Output Window I see an error message:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=CompanyItems; DataItem=null; target element is 'DataGridComboBoxColumn' (HashCode=28633162); target property is 'ItemsSource' (type 'IEnumerable')

I believe that WPF expects CompanyItems to be a property of GridItem which is not the case, and that's the reason why the binding fails.

I've already tried to work with a RelativeSource and AncestorType like so:

<DataGridComboBoxColumn ItemsSource="{Binding CompanyItems, 
    RelativeSource={RelativeSource Mode=FindAncestor,
                                   AncestorType={x:Type Window}}}"
                        DisplayMemberPath="Name"
                        SelectedValuePath="ID"
                        SelectedValueBinding="{Binding CompanyID}" />

But that gives me another error in the debugger output:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=CompanyItems; DataItem=null; target element is 'DataGridComboBoxColumn' (HashCode=1150788); target property is 'ItemsSource' (type 'IEnumerable')

Question: How can I bind the ItemsSource of the DataGridComboBoxColumn to the CompanyItems collection of the ViewModel? Is it possible at all?

Thank you for help in advance!

7条回答
无与为乐者.
2楼-- · 2019-01-01 06:17

I realize this question is over a year old, but I just stumbled across it in dealing with a similar problem and thought I would share another potential solution in case it might help a future traveler (or myself, when I forget this later and find myself flopping around on StackOverflow between screams and throwings of the nearest object on my desk).

In my case I was able to get the effect I wanted by using a DataGridTemplateColumn instead of a DataGridComboBoxColumn, a la the following snippet. [caveat: I'm using .NET 4.0, and what I've been reading leads me to believe the DataGrid has done a lot of evolving, so YMMV if using earlier version]

<DataGridTemplateColumn Header="Identifier_TEMPLATED">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox IsEditable="False" 
                Text="{Binding ComponentIdentifier,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                ItemsSource="{Binding Path=ApplicableIdentifiers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ComponentIdentifier}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
查看更多
登录 后发表回答