.net 在 WPF DataGrid 中绑定 ComboBoxColumn 的 ItemsSource
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5409259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Binding ItemsSource of a ComboBoxColumn in WPF DataGrid
提问by Slauma
I have two simple Model classes and a ViewModel...
我有两个简单的 Model 类和一个 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 DataContextin App.xaml.cs:
ViewModel 设置为DataContextApp.xaml.cs 中的 MainWindow :
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 ItemsSourceof the DataGrid to the GridItemscollection of the ViewModel. This part works, the single Grid line with Name "Jim" is displayed.
如您所见,我将ItemsSourceDataGrid 的设置GridItems为 ViewModel的集合。这部分工作,显示名称为“Jim”的单个网格线。
I also want to set the ItemsSourceof the ComboBox in every row to the CompanyItemscollection of the ViewModel. This part does not work: The ComboBox remains empty and in the Debugger Output Window I see an error message:
我还想将ItemsSource每一行中的 ComboBox设置CompanyItems为 ViewModel的集合。这部分不起作用:组合框保持为空,在调试器输出窗口中我看到一条错误消息:
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')
System.Windows.Data 错误:2:找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement。BindingExpression:Path=CompanyItems; 数据项=空;目标元素是“DataGridComboBoxColumn”(HashCode=28633162);目标属性是“ItemsSource”(类型“IEnumerable”)
I believe that WPF expects CompanyItemsto be a property of GridItemwhich is not the case, and that's the reason why the binding fails.
我相信 WPF 期望CompanyItems的属性GridItem并非如此,这就是绑定失败的原因。
I've already tried to work with a RelativeSourceand AncestorTypelike so:
我已经尝试过使用 aRelativeSource并且AncestorType像这样:
<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')
System.Windows.Data 错误:4:无法找到引用“RelativeSource FindAncestor、AncestorType='System.Windows.Window'、AncestorLevel='1'”的绑定源。BindingExpression:Path=CompanyItems; 数据项=空;目标元素是“DataGridComboBoxColumn”(HashCode=1150788);目标属性是“ItemsSource”(类型“IEnumerable”)
Question: How can I bind the ItemsSource of the DataGridComboBoxColumn to the CompanyItems collection of the ViewModel? Is it possible at all?
问题:如何将 DataGridComboBoxColumn 的 ItemsSource 绑定到 ViewModel 的 CompanyItems 集合?有可能吗?
Thank you for help in advance!
提前感谢您的帮助!
回答by serge_gubenko
Pls, check if DataGridComboBoxColumn xaml below would work for you:
请检查下面的 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>
Here you can find another solution for the problem you're facing: Using combo boxes with the WPF DataGrid
在这里,您可以找到解决您面临的问题的另一种解决方案:将组合框与 WPF DataGrid 结合使用
回答by Slauma
The documentation on MSDN about the ItemsSourceof the DataGridComboBoxColumnsays that only static resources, static code or inline collections of combobox items can be bound to the ItemsSource:
在关于对MSDN文档ItemsSource的DataGridComboBoxColumn说,只有静态的资源,组合框项目静态代码或内联的集合可以绑定到ItemsSource:
To populate the drop-down list, first set the ItemsSource property for the ComboBox by using one of the following options:
- A static resource. For more information, see StaticResource Markup Extension.
- An x:Static code entity. For more information, see x:Static Markup Extension.
- An inline collection of ComboBoxItem types.
要填充下拉列表,请首先使用以下选项之一设置 ComboBox 的 ItemsSource 属性:
- 静态资源。有关详细信息,请参阅静态资源标记扩展。
- x:静态代码实体。有关详细信息,请参阅 x:静态标记扩展。
- ComboBoxItem 类型的内联集合。
Binding to a DataContext's property is not possible if I understand that correctly.
如果我理解正确,则无法绑定到 DataContext 的属性。
And indeed: When I make CompanyItemsa staticproperty in ViewModel ...
事实上:当我在 ViewModel 中创建CompanyItems一个静态属性时......
public static ObservableCollection<CompanyItem> CompanyItems { get; set; }
... add the namespace where the ViewModel is located to the window ...
... 添加 ViewModel 所在的命名空间到窗口 ...
xmlns:vm="clr-namespace:DataGridComboBoxColumnApp"
... and change the binding to ...
... 并将绑定更改为 ...
<DataGridComboBoxColumn
ItemsSource="{Binding Source={x:Static vm:ViewModel.CompanyItems}}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValueBinding="{Binding CompanyID}" />
... then it works. But having the ItemsSource as a static property might be sometimes OK, but it is not always what I want.
......然后它起作用了。但是将 ItemsSource 作为静态属性有时可能没问题,但这并不总是我想要的。
回答by Adam Becker
The correct solution seems to be:
正确的解决方案似乎是:
<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>
The layout above works perfectly fine for me, and should work for others. This design choice also makes sense, though it isn't very well explained anywhere. But if you have a data column with predefined values, those values typically don't change during run-time. So creating a CollectionViewSourceand initializing the data once makes sense. It also gets rid of the longer bindings to find an ancestor and bind on it's data context (which always felt wrong to me).
上面的布局对我来说非常好,对其他人也应该有效。这种设计选择也很有意义,尽管在任何地方都没有很好地解释。但是,如果您有一个带有预定义值的数据列,这些值通常不会在运行时更改。因此,创建一个CollectionViewSource并初始化数据一次是有意义的。它还摆脱了较长的绑定以查找祖先并绑定到它的数据上下文(这对我来说总是错误的)。
I am leaving this here for anyone else who struggled with this binding, and wondered if there was a better way (As this page is obviously still coming up in search results, that's how I got here).
我把这个留在这里给那些在这个绑定上挣扎的人,并想知道是否有更好的方法(因为这个页面显然仍然出现在搜索结果中,这就是我来到这里的方式)。
回答by Rick Riensche
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).
我意识到这个问题已经有一年多了,但我只是在处理类似问题时偶然发现了它,并认为我会分享另一个潜在的解决方案,以防它可能对未来的旅行者(或我自己,当我后来忘记这一点并发现自己时)有所帮助在我的桌子上最近的物体的尖叫和投掷之间在 StackOverflow 上翻来覆去)。
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 而不是 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>
回答by Benoit Blanchon
RookieRick is right, using DataGridTemplateColumninstead of DataGridComboBoxColumngives a much simpler XAML.
RookieRick 是对的,使用DataGridTemplateColumn而不是DataGridComboBoxColumn提供更简单的 XAML。
Moreover, putting the CompanyItemlist directly accessible from the GridItemallows you to get rid of the RelativeSource.
此外,将CompanyItem列表直接从GridItem可访问允许您摆脱RelativeSource.
IMHO, this give you a very clean solution.
恕我直言,这给了你一个非常干净的解决方案。
XAML:
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>
View model:
查看型号:
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; }
}
回答by Rachel
Your ComboBox is trying to bind to bind to GridItem[x].CompanyItems, which doesn't exist.
您的 ComboBox 正在尝试绑定到GridItem[x].CompanyItems不存在的绑定到。
Your RelativeBinding is close, however it needs to bind to DataContext.CompanyItemsbecause Window.CompanyItems does not exist
您的 RelativeBinding 已关闭,但它需要绑定到,DataContext.CompanyItems因为 Window.CompanyItems 不存在
回答by Hisham
the bast way i use i bind the textblock and combobox to same property and this property should support notifyPropertyChanged.
我使用的最简单的方法是将文本块和组合框绑定到相同的属性,并且此属性应支持 notifyPropertyChanged。
i used relativeresource to bind to parent view datacontext which is usercontrol to go up datagrid level in binding because in this case the datagrid will search in object that you used in datagrid.itemsource
我使用relativeresource 绑定到父视图datacontext,这是usercontrol 在绑定中提升datagrid 级别,因为在这种情况下datagrid 将搜索您在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>

