【问题标题】:Cannot use DataContext of parent from within styles无法在样式中使用父级的 DataContext
【发布时间】:2018-04-03 11:28:39
【问题描述】:

我在从 Style 中访问特定 DataContext 时遇到了一些问题。
我有一个这样定义的DataGrid

<DataGrid Name="ReferenceDataGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False" RowHeaderWidth="0" IsReadOnly="True">
    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem Header="Delete" Command="???" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGrid.Resources>
    ...

包含此DataGridPageDataContext 属性设置为

DataContext="{Binding RelativeSource={RelativeSource Self}}"

我想将RelayCommand 绑定到MenuItem 并尝试了几种不同的方法来做到这一点:

  • Command="{Binding DeleteCommand}"
  • Command="{Binding ElementName=Root, Path=DeleteCommand}" // 页面元素名称设置为 Root
  • Command="{Binding ElementName=Root, Path=DataContext.DeleteCommand}"
  • Command="{Binding Path=DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}"

我在DeleteCommand 的命令处理程序中设置了一个断点,但上述变体都没有到达那里。

假设Items 包含Foo 类型的元素。
如果我在Foo 中定义命令,则处理程序会被触发,因此似乎无论我在上面做了什么,每个DataGridRowDataContext 似乎都是列表元素本身。

知道该怎么做吗?

编辑:
我还尝试将DataContext 提取到一个单独的类中并引用它而不是Relative Self,因为我认为列表元素可能使用Relative Self 作为它们的DataContext,而不是它所引用的Page 实例。可惜我错了。

【问题讨论】:

  • 不,也不起作用。也许我必须做一些不同的事情,因为我使用的是样式而不是数据模板?
  • 您是否只会使用与 DataGrid 在同一数据上下文中的命令?
  • @Andy 到目前为止,我总是每个页面/窗口等只有一个 DataContext。我真的不想用这些东西弄乱模型类,所以我将它们存储在页面的 DataContext 中。我还需要更新 DataContext 中的其他几个属性,因此将其部分提取到单独的属性中不会带来任何好处
  • 其实……这是件好事。我会发布一个解决方案。

标签: c# wpf binding


【解决方案1】:

由于您只对某个地方的命令感兴趣,因此您可能会发现另一种有用的方法。 将上下文菜单定义为可以获取该数据上下文的资源,然后将其应用于数据网格行。 我在一个原本用于其他目的的样本上完成了此操作,所以我的对象和东西是不同的。

<DataGrid 
    ItemsSource="{Binding Users}" 
    Background="White" 
    Name="dg"
    SelectedItem="{Binding SelectedUser}"
          >
    <DataGrid.Resources>
        <ContextMenu x:Key="dgContextMenu"
                     DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}">
            <MenuItem Header="Up" Command="{Binding UpCommand}" />
        </ContextMenu>
    </DataGrid.Resources>
    <DataGrid.RowStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="ContextMenu" Value="{StaticResource dgContextMenu}"/>

DataContext 继承了可视化树,因此数据网格获得与页面或窗口或其他任何内容相同的数据上下文。 我的上下文菜单实际上是在数据网格中,可以得到它的数据上下文。

我还绑定了 selecteditem,因此我可以使用它来确定点击了哪个特定行。

在窗口的视图模型中,我有一个命令“UpCommand”,它使用所选用户的属性。

    public RelayCommand UpCommand { get; set; }
    public MainWindowViewModel()
    {
          UpCommand = new RelayCommand(UpExecute);
    }
    public User SelectedUser { get; set; }
    private void UpExecute()
    {
        MessageBox.Show($"You Upped {SelectedUser.Title}");
    }

您可以从用户中删除选定的用户,或者使用它的 ID 从数据库中删除记录或执行任何删除操作。

【讨论】:

    【解决方案2】:
    <ContextMenu  DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
        <MenuItem Header="Delete"  Command="{Binding DataContext.DeleteCommand}" />
    </ContextMenu>
    

    正如您在回答重复问题时看到的那样,问题是:

    ContextMenu 位于可视化树之外

    如果您不想将DeleteCommand 放入对象(这是Items 中的一个条目),则可以使用x:ReferenceBinding 设置Source 属性。

    <DataGrid Name="ReferenceDataGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False" RowHeaderWidth="0" IsReadOnly="True">
        <DataGrid.Resources>
            <Style TargetType="DataGridRow">
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu  DataContext="{Binding Path=DataContext, Source={x:Reference ReferenceDataGrid}}">
                            <MenuItem Header="Delete"  Command="{Binding DeleteCommand}" />
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.Resources>
    

    【讨论】:

    • 我已经尝试过这个解决方案,但它不像我在 cmets 中所说的那样工作。
    • 它对我有用。你确定你在对象中有一个 GridRowDataContextDeleteCmd,它是 DataGridRow 的 DataContext?
    • 请查看DataContextContextMenu 已设置。
    • 我正在尝试重现该问题,但它显示错误 4。我还尝试了您使用 PresentationTraceSources.TraceLevel=High 的解决方案,但无济于事。我认为事件处理程序可能是这里的解决方案。
    • @Rekshino 我复制粘贴了您的代码,以确保我没有错别字,但它仍然无法正常工作。命令实例存在。我在 DataGrid 旁边创建了一个按钮,让它成功触发这个命令,所以我假设命令实例本身不是问题。
    猜你喜欢
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 2022-01-26
    相关资源
    最近更新 更多