Wpf 在ViewModel的DataTemplate中定义的DataGrid的DataGridColumnHeader
我已成功使用ProxyElement将数据网格的数据上下文传递给DataGridColumnHeaders。然而,我正在尝试一些新的东西,我只是不知道我在这里做错了什么 下面是我试图做的:我正在创建一个UserControl,并将其与我的资源文件中的ViewModel关联(请参阅下面的Resources.xaml代码片段) Resources.xaml:Wpf 在ViewModel的DataTemplate中定义的DataGrid的DataGridColumnHeader,wpf,xaml,datagrid,datatemplate,Wpf,Xaml,Datagrid,Datatemplate,我已成功使用ProxyElement将数据网格的数据上下文传递给DataGridColumnHeaders。然而,我正在尝试一些新的东西,我只是不知道我在这里做错了什么 下面是我试图做的:我正在创建一个UserControl,并将其与我的资源文件中的ViewModel关联(请参阅下面的Resources.xaml代码片段) Resources.xaml: <ResourceDictionary xmlns:myVm="clr-namespace:..." xmlns:myU
<ResourceDictionary
xmlns:myVm="clr-namespace:..."
xmlns:myUserControl="clr-namespace:...">
<DataTemplate DataType={x:Type myVm:DummyModel}">
<myUserControl:DummyUserControl />
</DataTemplate>
</ResourceDictionary>
我不知道在这里该做什么。我记得读到datatemplate的datacontext是自动设置的,所以我不知道为什么在这种情况下数据上下文为null。为了证明它为null,我还尝试在代码隐藏文件中设置绑定,并添加了一个断点来检查其值(为null)
有人能建议在这里做什么吗
编辑1
我还尝试了以下方法:
- 完全删除ProxyElement并查看它是否可以检测DataContext。不足为奇的是,这失败了
- 已尝试绑定到模板化父级。失败。
- 尝试绑定到UserControl本身。失败
- 我还尝试引用要显示此视图模型的父项的数据上下文,该父项位于TabControl的TabItem中。
<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>
这里有一个解决这个问题的(可行但不是首选的)解决方案。到最后你会明白为什么它不是首选
这个问题的关键是理解数据模板的数据上下文是什么以及如何工作的。无论何时为视图模型定义数据模板,随后视图的数据上下文(无论是用户控件还是xaml本身)都是视图模型!这对任何人都不应该感到惊讶
但这会让人们感到惊讶:如果您指定了一个用户控件,那么用户控件的数据上下文在用户控件的构造过程中不会被设置!换句话说,在用户控件的构造函数中,数据上下文将为null。此外,任何在构造时依赖于数据上下文的XAML代码(在本例中是名为ProxyElement的FrameworkElement资源)都将其DataContext设置为null,因为它是在用户控件的构造时构造的
那么什么时候设置DataContext呢?仅在创建用户控件之后。在伪代码中,以下描述了绘制ViewModel背后的逻辑:
绘制视图模型x李>
ResourceDictionary中的DataTemplate表示可以使用UserControl abc绘制ViewModel x
让我们创建UserControl abc的一个新实例
现在让我们将abc的DataContext分配给ViewModel本身
让我们返回新创建的UserControl abc实例
那么我们该如何解决这个问题呢
UserControl.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>
<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>
我正在试图找出一种方法来摆脱代码隐藏文件中的代码。但在此之前,如果其他人遇到了这个问题,那么他们可能会从这个解决方案中得到启发
这个解决方案背后的概念是:我应该如何更改ProxyElement的声明?哦,是的,您在其中设置了源代码。但是我认为没有必要在绑定中提供源代码,让Combobox直接从上面获取DataContext。我更新了答案它不起作用。这是我得到的错误:System.Windows.Data错误:2:找不到目标元素的治理FrameworkElement或FrameworkContentElement。BindingExpression:Path=productcompanys;DataItem=null;目标元素是“DataGridComboxColumn”(HashCode=22979266);目标属性为“ItemsSource”(类型为“IEnumerable”)您的意思是从绑定中删除源后出现错误?是的。我确实试过你建议的解决办法。我已经在“编辑1”下列出了它,但不知何故,我所有的代码片段都从编辑中删除了。
<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>
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
}