绑定WPF DataGrid中ComboBoxColumn的ItemsSource

我有两个简单的模型类和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; } } 

…和一个简单的窗口:

 <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> 

ViewModel被设置为App.xaml.cs中的MainWindow的DataContext

 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(); } } 

正如你所看到的,我将DataGrid的ItemsSource设置为ViewModel的GridItems集合。 这部分工作,显示名为“Jim”的单个网格线。

我也想把每一行中的ComboBox的ItemsSource设置为ViewModel的CompanyItems集合。 这部分不起作用:combobox保持空白,并在debugging器输出窗口中看到一条错误消息:

System.Windows.Data错误:2:找不到控制目标元素的FrameworkElement或FrameworkContentElement。 BindingExpression:path= CompanyItems; 的DataItem = NULL; 目标元素是'DataGridComboBoxColumn'(HashCode = 28633162); 目标属性是'ItemsSource'(types'IEnumerable')

我相信WPF希望CompanyItemsGridItem的属性,而不是这种情况,这就是绑定失败的原因。

我已经尝试使用RelativeSourceAncestorType如下所示:

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

但是,这给了我另一个错误在debugging器输出:

System.Windows.Data错误:4:无法find与参考'RelativeSource FindAncestor,AncestorType ='System.Windows.Window',AncestorLevel ='1'绑定的源。 BindingExpression:path= CompanyItems; 的DataItem = NULL; 目标元素是'DataGridComboBoxColumn'(HashCode = 1150788); 目标属性是'ItemsSource'(types'IEnumerable')

问:如何将DataGridComboBoxColumn的ItemsSource绑定到ViewModel的CompanyItems集合? 有没有可能?

提前感谢您的帮助!

请检查下面的DataGridComboBoxColumn xaml是否适合您:

 <DataGridComboBoxColumn SelectedValueBinding="{Binding CompanyID}" DisplayMemberPath="Name" SelectedValuePath="ID"> <DataGridComboBoxColumn.ElementStyle> <Style TargetType="{x:Type ComboBox}"> <Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" /> </Style> </DataGridComboBoxColumn.ElementStyle> <DataGridComboBoxColumn.EditingElementStyle> <Style TargetType="{x:Type ComboBox}"> <Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" /> </Style> </DataGridComboBoxColumn.EditingElementStyle> </DataGridComboBoxColumn> 

在这里你可以find另一个解决scheme,你正面临的问题: 与WPF数据网格一起使用combobox

希望这有助于问候

MSDN上有关DataGridComboBoxColumnItemsSource的文档说,只有静态资源,静态代码或内联项目的集合可以绑定到ItemsSource

要填充下拉列表,首先使用以下选项之一设置ComboBox的ItemsSource属性:

  • 一个静态资源。 有关更多信息,请参阅StaticResource标记扩展。
  • 一个x:静态代码实体。 有关更多信息,请参见x:静态标记扩展。
  • ComboBoxItemtypes的内联集合。

如果我理解正确的话,绑定到DataContext的属性是不可能的。

事实上:当我在ViewModel中使CompanyItems成为一个静态属性…

 public static ObservableCollection<CompanyItem> CompanyItems { get; set; } 

将ViewModel所在的命名空间添加到窗口中

 xmlns:vm="clr-namespace:DataGridComboBoxColumnApp" 

…并将绑定更改为…

 <DataGridComboBoxColumn ItemsSource="{Binding Source={x:Static vm:ViewModel.CompanyItems}}" DisplayMemberPath="Name" SelectedValuePath="ID" SelectedValueBinding="{Binding CompanyID}" /> 

…然后它的工作。 但是将ItemsSource作为一个静态属性可能有时可以,但并不总是我想要的。

我意识到这个问题已经过了一年多了,但是我在处理类似问题时偶然发现了这个问题,并且认为我会分享另一个潜在的解决scheme,以便可以帮助未来的旅行者(或者我自己,当我以后忘记这一点,发现自己在我桌上最近的物体的尖叫声和抛掷声之间翻转着StackOverflow)。

在我的情况下,我能够通过使用DataGridTemplateColumn,而不是DataGridComboBoxColumn,下面的代码片段得到我想要的效果。 [警告:我正在使用.NET 4.0,而且我一直在阅读的内容让我相信DataGrid已经做了很多的改进,所以YMMV如果使用早期的版本]

 <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> 

正确的解决scheme似乎是:

 <Window.Resources> <CollectionViewSource x:Key="ItemsCVS" Source="{Binding MyItems}" /> </Window.Resources> <!-- ... --> <DataGrid ItemsSource="{Binding MyRecords}"> <DataGridComboBoxColumn Header="Column With Predefined Values" ItemsSource="{Binding Source={StaticResource ItemsCVS}}" SelectedValueBinding="{Binding MyItemId}" SelectedValuePath="Id" DisplayMemberPath="StatusCode" /> </DataGrid> 

上面的布局对我来说工作得很好,应该为别人工作。 这个deviseselect也是有道理的,尽pipe在任何地方都没有很好的解释。 但是,如果您有一个预定义值的数据列,这些值通常不会在运行时更改。 所以创build一个CollectionViewSource并初始化数据是有道理的。 它也摆脱了较长的绑定,以find一个祖先,并绑定在它的数据上下文(我总是觉得不对)。

我将这里留给了那些为了这个绑定而挣扎的人,并且想知道是否有更好的方法(因为这个页面显然还在search结果中,所以我就是这么来的)。

RookieRick是正确的,使用DataGridTemplateColumn而不是DataGridComboBoxColumn给出了一个更简单的XAML。

而且,从GridItem直接访问CompanyItem列表可以让你摆脱RelativeSource

恕我直言,这给你一个非常干净的解决scheme。

XAML:

 <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" > <DataGrid.Resources> <DataTemplate x:Key="CompanyDisplayTemplate" DataType="vm:GridItem"> <TextBlock Text="{Binding Company}" /> </DataTemplate> <DataTemplate x:Key="CompanyEditingTemplate" DataType="vm:GridItem"> <ComboBox SelectedItem="{Binding Company}" ItemsSource="{Binding CompanyList}" /> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Name}" /> <DataGridTemplateColumn CellTemplate="{StaticResource CompanyDisplayTemplate}" CellEditingTemplate="{StaticResource CompanyEditingTemplate}" /> </DataGrid.Columns> </DataGrid> 

查看模型:

 public class GridItem { public string Name { get; set; } public CompanyItem Company { get; set; } public IEnumerable<CompanyItem> CompanyList { get; set; } } public class CompanyItem { public int ID { get; set; } public string Name { get; set; } public override string ToString() { return Name; } } public class ViewModel { readonly ObservableCollection<CompanyItem> companies; public ViewModel() { companies = new ObservableCollection<CompanyItem>{ new CompanyItem { ID = 1, Name = "Company 1" }, new CompanyItem { ID = 2, Name = "Company 2" } }; GridItems = new ObservableCollection<GridItem> { new GridItem { Name = "Jim", Company = companies[0], CompanyList = companies} }; } public ObservableCollection<GridItem> GridItems { get; set; } } 

您的ComboBox试图绑定到GridItem[x].CompanyItems ,它不存在。

您的RelativeBinding是closures的,但它需要绑定到DataContext.CompanyItems因为Window.CompanyItems不存在

我使用的韧皮方式我将textblock和combobox绑定到相同的属性,这个属性应该支持notifyPropertyChanged。

我用relativeresource绑定到父视图datacontext这是usercontrol在绑定中上升datagrid级别,因为在这种情况下,datagrid将search您在datagrid.itemsource中使用的对象

 <DataGridTemplateColumn Header="your_columnName"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit.Name, Mode=TwoWay}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellEditingTemplate> <DataTemplate> <ComboBox DisplayMemberPath="Name" IsEditable="True" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.UnitLookupCollection}" SelectedItem="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValue="{Binding UnitId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Id" /> </DataTemplate> </DataGridTemplateColumn.CellEditingTemplate> </DataGridTemplateColumn>