绑定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希望CompanyItems
是GridItem
的属性,而不是这种情况,这就是绑定失败的原因。
我已经尝试使用RelativeSource
和AncestorType
如下所示:
<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上有关DataGridComboBoxColumn
的ItemsSource
的文档说,只有静态资源,静态代码或内联项目的集合可以绑定到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>