在DataTemplate中为ViewModel定义的DataGrid的DataGridColumnHeader

时间:2014-02-27 14:14:14

标签: wpf xaml datagrid datatemplate

我已成功使用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)。

有谁能建议在这做什么?

编辑1

我也尝试了以下方法:

  • 完全删除ProxyElement并查看它是否可以检测到DataContext。毫不奇怪,这失败了。

  • 尝试绑定到模板化父级。失败。

  • 尝试绑定到UserControl本身。失败。

  • 我还尝试引用将要显示此视图模型的父项的数据上下文,它位于TabControl的TabItem中。

所有备用绑定都给出了与上述错误相同的错误。

2 个答案:

答案 0 :(得分:1)

这是解决此问题的(有效但不是优选的)解决方案。你会意识到为什么在它结束时不喜欢它。

此问题的关键是了解数据模板的数据上下文的工作原理和方式。无论何时为视图模型定义数据模板,后面的视图的数据上下文,无论是用户控件还是xaml本身,都是视图模型!这对任何人来说都不应该是一个惊喜。

但这会让人感到惊讶:如果您指定了用户控件,则在构建用户控件期间不会设置用户控件的数据上下文!换句话说,在User Control的构造函数中,Data Context将为null。此外,在构造时依赖于数据上下文的任何XAML代码(在本例中是我的FrameworkElement资源,称为ProxyElement)将其DataContext设置为null,因为它是在用户控件的构造时构造的!

那么DataContext什么时候开始设置?只需在创建用户控件之后。在伪代码中,以下描述了绘制ViewModel的原理:

  1. 绘制ViewModel x;
  2. ResourceDictionary中的DataTemplate说可以使用UserControl abc绘制ViewModel x
  3. 让我们创建一个新的UserControl abc实例
  4. 现在让我们将abc的DataContext分配给ViewModel本身。
  5. 让我们返回新创建的UserControl abc实例
  6. 那么我们该如何解决这个问题呢?

    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我希望这会有所帮助。