我已成功使用ProxyElement将Data Grid的Data Context传递给DataGridColumnHeaders。但是,我正在尝试一些新的东西,我无法弄清楚我在这里做错了什么。
以下是我要做的事情:我正在创建一个UserControl并将其与我的Resources文件中的ViewModel相关联(请参阅下面的参考资料.xaml代码片段)。
Resources.xaml:
<ResourceDictionary
xmlns:myVm="clr-namespace:..."
xmlns:myUserControl="clr-namespace:...">
<DataTemplate DataType={x:Type myVm:DummyModel}">
<myUserControl:DummyUserControl />
</DataTemplate>
</ResourceDictionary>
现在在我的UserControl中,我有一个带DataGridComboBoxColumn的DataGrid。我试图访问我的数据上下文来设置其项目源,并在过去我能够使用代理元素。但这次我无法这样做。 (请参阅下面的DummyUserControl.xaml代码段)
DummyUserControl.xaml:
<UserControl x:Class="Client.MyControl.DummyUserControl"
...>
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" x:Name="ProxyElement"
DataContext="{Binding}" />
</UserControl.Resources>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
<DataGridComboBoxColumn
Header="Company"
ItemsSource="{Binding Path=DataContext.ProductCompanies,
Source={StaticResource ProxyElement}}"
DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedValueBinding="{Binding CompanyId}" />
</DataGrid>
</UserControl>
当我这样做时,我的绑定失败并显示以下消息:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext.
BindingExpression:(no path); DataItem=null; target element is 'FrameworkElement'
(Name='ProxyElement'); target property is 'DataContext' (type 'Object')
我不知道该怎么做。我记得读过datatemplate的datacontext是自动设置的,所以我不知道为什么数据上下文在这种情况下是null。为了证明它是null,我还尝试在代码隐藏文件中设置绑定并添加一个断点来检查其值(为null)。
有谁能建议在这做什么?
我也尝试了以下方法:
完全删除ProxyElement并查看它是否可以检测到DataContext。毫不奇怪,这失败了。
尝试绑定到模板化父级。失败。
尝试绑定到UserControl本身。失败。
我还尝试引用将要显示此视图模型的父项的数据上下文,它位于TabControl的TabItem中。
所有备用绑定都给出了与上述错误相同的错误。
答案 0 :(得分:1)
这是解决此问题的(有效但不是优选的)解决方案。你会意识到为什么在它结束时不喜欢它。
此问题的关键是了解数据模板的数据上下文的工作原理和方式。无论何时为视图模型定义数据模板,后面的视图的数据上下文,无论是用户控件还是xaml本身,都是视图模型!这对任何人来说都不应该是一个惊喜。
但这会让人感到惊讶:如果您指定了用户控件,则在构建用户控件期间不会设置用户控件的数据上下文!换句话说,在User Control的构造函数中,Data Context将为null。此外,在构造时依赖于数据上下文的任何XAML代码(在本例中是我的FrameworkElement资源,称为ProxyElement)将其DataContext设置为null,因为它是在用户控件的构造时构造的!
那么DataContext什么时候开始设置?只需在创建用户控件之后。在伪代码中,以下描述了绘制ViewModel的原理:
那么我们该如何解决这个问题呢?
UserControl.xaml:
<UserControl ...
DataContextChanged="DaCoHasChanged">
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" /> <!--Remove DataContext="{Binding}"-->
</UserControl.Resources>
<DataGrid ...>
<DataGridComboBoxColumn
ItemsSource="{Binding Path=DataContext.ProductCompanies,
Source={StaticSource ProxyElement}}"
... />
</DataGrid>
</UserControl>
UserControl.xaml.cs:
private void DaCoHasChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var proxyElement = Resources["ProxyElement"] as FrameworkElement;
proxyElement.DataContext = e.NewValue; // instead of e.NewValue, you could
// also say this.DataContext
}
我试图找出一种摆脱代码隐藏文件中的代码的方法。但在此之前,如果其他人遇到这个问题,那么他们可能会从这个解决方案中受到启发。
归功于此解决方案背后的概念:How to set the DataContext for a View created in DataTemplate from ViewModel
答案 1 :(得分:0)
试试这个
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
<DataGridTemplateColumn Header="Company">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path="{Binding DataContext.ProductCompanies,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedValueBinding="{Binding CompanyId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
现在我遇到了你的问题我觉得问题出在DataGridComboBoxColumn我不知道为什么它不使用RelativeResource绑定。尝试使用 DataGridTemplateColumn ,您不需要任何ProxyElement我希望这会有所帮助。