在 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 的 code>ItemsSource 到 ViewModel 的 GridItems
集合。这部分起作用了,显示了名为“Jim”的单条网格线。
我还想将每行中 ComboBox 的 ItemsSource
设置为 ViewModel 的 CompanyItems
集合。这部分不起作用:组合框保持为空,并且在调试器输出窗口中我看到一条错误消息:
System.Windows.Data 错误:2:无法 找到管理 FrameworkElement 或 目标的 FrameworkContentElement 元素。 BindingExpression:Path=CompanyItems; 数据项=空;目标元素是 'DataGridComboBox列' (哈希码=28633162);目标财产 是“ItemsSource”(类型“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}" />
但这在调试器输出中给了我另一个错误:
System.Windows.Data 错误:4:无法 查找参考绑定源 '相对源查找祖先, AncestorType='System.Windows.Window', 祖先等级='1''。 BindingExpression:Path=CompanyItems; 数据项=空;目标元素是 'DataGridComboBox列' (哈希码=1150788);目标属性是 “ItemsSource”(类型“IEnumerable”)
问题:如何将 DataGridComboBoxColumn 的 ItemsSource 绑定到 ViewModel 的 CompanyItems 集合?有可能吗?
提前感谢您的帮助!
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
请检查下面的 DataGridComboBoxColumn xaml 是否适合您:
在这里您可以找到解决您所面临问题的另一种解决方案:在 WPF 中使用组合框数据网格
Pls, check if DataGridComboBoxColumn xaml below would work for you:
Here you can find another solution for the problem you're facing: Using combo boxes with the WPF DataGrid
正确的解决方案似乎是:
上面的布局对我来说非常适合,并且应该对其他人也适用。这种设计选择也是有道理的,尽管在任何地方都没有得到很好的解释。但是,如果您有一个包含预定义值的数据列,那么这些值通常不会在运行时更改。因此,创建一个 CollectionViewSource 并初始化一次数据是有意义的。它还摆脱了较长的绑定来查找祖先并绑定到其数据上下文(这对我来说总是感觉不对)。
我将这个留给那些在这种绑定上苦苦挣扎的人,并想知道是否有更好的方法(因为这个页面显然仍然出现在搜索结果中,这就是我到达这里的方式)。
The correct solution seems to be:
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
CollectionViewSource
and 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).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).
MSDN 上有关
ItemsSource
的文档>DataGridComboBoxColumn 表示只有静态资源、静态代码或组合框项的内联集合可以绑定到ItemsSource
:如果我理解正确的话,则不可能绑定到 DataContext 的属性。
事实上:当我在 ViewModel 中将
CompanyItems
设为 static 属性时……将 ViewModel 所在的命名空间添加到窗口中……
并且将绑定更改为 ...
... 然后就可以了。但是将 ItemsSource 作为静态属性有时可能没问题,但这并不总是我想要的。
The documentation on MSDN about the
ItemsSource
of theDataGridComboBoxColumn
says that only static resources, static code or inline collections of combobox items can be bound to theItemsSource
:Binding to a DataContext's property is not possible if I understand that correctly.
And indeed: When I make
CompanyItems
a static property in ViewModel ...... add the namespace where the ViewModel is located to the window ...
... and change the binding to ...
... then it works. But having the ItemsSource as a static property might be sometimes OK, but it is not always what I want.
我意识到这个问题已经存在一年多了,但我只是在处理类似问题时偶然发现了它,并认为我会分享另一个潜在的解决方案,以防它可能对未来的旅行者(或我自己,当我后来忘记这一点并发现自己时)有帮助在 StackOverflow 上翻来覆去,一边尖叫一边把最近的物体扔到我的桌子上)。
就我而言,我能够通过使用 DataGridTemplateColumn 而不是 DataGridComboBoxColumn 来获得我想要的效果,如以下代码片段所示。 [警告:我正在使用 .NET 4.0,我所阅读的内容让我相信 DataGrid 已经做了很多改进,所以如果使用早期版本,YMMV]
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]
RookieRick 是对的,使用
DataGridTemplateColumn
而不是DataGridComboBoxColumn
可以提供更简单的 XAML。此外,通过将
CompanyItem
列表直接从GridItem
访问,您可以摆脱RelativeSource
。恕我直言,这为您提供了一个非常干净的解决方案。
XAML:
视图模型:
RookieRick is right, using
DataGridTemplateColumn
instead ofDataGridComboBoxColumn
gives a much simpler XAML.Moreover, putting the
CompanyItem
list directly accessible from theGridItem
allows you to get rid of theRelativeSource
.IMHO, this give you a very clean solution.
XAML:
View model:
您的 ComboBox 正在尝试绑定到
GridItem[x].CompanyItems
,但它不存在。您的RelativeBinding很接近,但是它需要绑定到
DataContext.CompanyItems
,因为Window.CompanyItems不存在Your ComboBox is trying to bind to bind to
GridItem[x].CompanyItems
, which doesn't exist.Your RelativeBinding is close, however it needs to bind to
DataContext.CompanyItems
because Window.CompanyItems does not exist我使用的最简单的方法是将文本块和组合框绑定到同一属性,并且该属性应该支持notifyPropertyChanged。
我使用relativeresource绑定到父视图数据上下文,这是用户控件,以在绑定中提升数据网格级别,因为在这种情况下,数据网格将在您在datagrid.itemsource中使用的对象中搜索
the bast way i use i bind the textblock and combobox to same property and this property should support 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
这对我有用:
This is working for me: