如何在 wpf 的分层数据模板中显示树视图项的上下文菜单

Posted

技术标签:

【中文标题】如何在 wpf 的分层数据模板中显示树视图项的上下文菜单【英文标题】:How to display context menu for treeview item in a hierarchial data template in wpf 【发布时间】:2012-11-05 10:31:17 【问题描述】:

如何使用分层数据模板在 wpf 中显示树视图项的上下文菜单?如何仅为 CountryTemplate 显示上下文菜单:

  <HierarchicalDataTemplate  x:Key="DispTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Binding Path=Label" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description" Tag="Binding Path=Tag">
            </TextBlock>
        </StackPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate  x:Key="BuildingTemplate"  ItemsSource="Binding Path=Building" ItemTemplate="StaticResource BuildingTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Binding Path=Label" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description"/>
        </StackPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate  x:Key="CityTemplate"  ItemsSource="Binding Path=City" ItemTemplate="StaticResource CityTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Binding Path=Label" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description"/>
        </StackPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="Binding Path=Country" ItemTemplate="StaticResource CountryTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Binding Path=RootName" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description"/>
        </StackPanel>
    </HierarchicalDataTemplate>

【问题讨论】:

【参考方案1】:

您还可以将ContextMenu 添加到数据模板中的任何可视子项,例如:

<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="Binding Path=Country" ItemTemplate="StaticResource CountryTemplate">
    <StackPanel Orientation="Horizontal">
        <StackPanel.ContextMenu>
            <ContextMenu>
                 <MenuItem Header="Header" Command="Binding Command"/> <!--This command should be in the data context (each country item)-->
            </ContextMenu>
        </StackPanel.ContextMenu>
        <TextBlock Text="Binding Path=RootName" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description"/>
    </StackPanel>
</HierarchicalDataTemplate>

【讨论】:

如何绑定到视图模型根目录中定义的命令。到目前为止 FindAncestor 方法不起作用。你能给我举个例子吗? 在我无法轻松访问视图模型以进行绑定的情况下,我尝试使用此 FindAncestor 绑定:Command="Binding DataContext.Command, RelativeSource=RelativeSource FindAncestor, AncestorType= x:Type MainWindow",一般主窗口的datacontext有根视图模型,有你想要的命令。 这不适用于上下文菜单,因为它不是可视化树的一部分。 是的,你是对的,那么你应该从你的视图模型中发出命令。也许使用一种消息(mvvm light 或由您自己实现)或为您的所有视图模型层次结构引发事件,这是一种复杂的方式。 是的,即使是像这样的小东西 XAML 也是一个真正的痛苦。我必须想出超出正常范围的解决方案。【参考方案2】:

上下文菜单无法正常工作的原因之一是,默认情况下,它们位于与其他所有内容不同的可视化树中,因此无法找到 DataContext

关键的见解是创建一个定义上下文菜单的&lt;Style&gt;, 然后将该样式附加到目标元素,该元素连接上下文 菜单。 这会将上下文菜单转换为一个可视树,该树与您想要的默认 DataContext 对齐。

首先,创建样式:

<UserControl.Resources>                                                                                                                        
    <ResourceDictionary>

        <!-- For the context menu to work, we must shift it into a style, which means that the context menu is now in a
        visual tree that is more closely related to the current data context. All we have to do then is set the style, 
        which hooks up the context menu. -->
        <Style x:Key="ContextMenuStyle" TargetType="x:Type StackPanel">
            <Setter Property="ContextMenu" Value="DynamicResource TreeViewContextMenu"/>
        </Style>
        <ContextMenu x:Key="TreeViewContextMenu">
            <MenuItem Header="Test" Command="Binding RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type UserControl, Path=DataContext.CmdDisplayDetailsGraph"/>
        </ContextMenu>

然后,将上下文菜单挂在您想要的任何位置,而不会遇到由不同视觉树引起的问题。

示例 1:

<HierarchicalDataTemplate DataType="x:Type snapshot:Details" ItemsSource="Binding DetailsList">
    <StackPanel Orientation="Vertical" Style="StaticResource ContextMenuStyle">
        <ContentPresenter Content="Binding" ContentTemplate="Binding View.DefaultDataRowTemplate" />
</StackPanel>

示例 2:

<DataTemplate DataType="x:Type snapshot:InstrumentDetails">
  <StackPanel Orientation="Vertical" Style="StaticResource ContextMenuStyle">                 
      <Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">

【讨论】:

【参考方案3】:
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="Binding Path=Country" ItemContainerStyle="StaticResource CountryTemplateItemContainerStyle" ItemTemplate="StaticResource CountryTemplate">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Binding Path=RootName" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description" />
                        </StackPanel>
</HierarchicalDataTemplate>


<Style x:Key="CountryTemplateItemContainerStyle" TargetType="x:Type TreeViewItem">
<Setter Property="ContextMenu" Value="DynamicResource TreeViewContextMenu"/>
</Style>
 <ContextMenu x:Key="TreeViewContextMenu">
        <MenuItem .../>
 </ContextMenu>

如您所见,您可以在 HierarchicalDataTemplate 的 Itemcontainerstyle 中添加上下文菜单

【讨论】:

这对我来说非常有用,而且,我喜欢 ContextMenu 是如何在项目附近定义的。 这对我有用。为了避免编译错误,我不得不交换块(确保 XAML 标记 HierarchicalDataTemplateStyleContextMenu 标记之后)。它起作用的原因是因为将 ContextMenu 转换为样式会将所有内容转换为更密切相关的可视化树,这意味着可以轻松找到 DataContext。【参考方案4】:

基本上我想出了这个

<HierarchicalDataTemplate  x:Key="ChildTemplate">
            <StackPanel Orientation="Horizontal">
                <StackPanel.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Copy" CommandParameter="Binding CopyTag">
                        </MenuItem>
                        <MenuItem Header="Paste" CommandParameter="Binding PasteTag">
                        </MenuItem>
                        <ContextMenu.ItemContainerStyle>
                            <Style TargetType="MenuItem">
                                <Setter Property="Command" Value="Binding RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type UserControl, Path=DataContext.CopyPaste"/>
                            </Style>
                        </ContextMenu.ItemContainerStyle>
                    </ContextMenu>
                </StackPanel.ContextMenu>
                <Image Source="/Images/Child.png" Stretch="None" VerticalAlignment="Center" HorizontalAlignment="Center" Style="StaticResource TreeIconStyle"/>
                <TextBlock Text="Binding Path=Label" Style="StaticResource TreeTextStyle" ToolTip="Binding Path=Description" Tag="Binding Path=Tag">
                </TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>

并具有单独的复制和粘贴参数,以区分单个命令中的复制和粘贴。

【讨论】:

以上是关于如何在 wpf 的分层数据模板中显示树视图项的上下文菜单的主要内容,如果未能解决你的问题,请参考以下文章

在具有分层数据模板的树视图中绑定上下文菜单命令

当所有子项都折叠或隐藏时,如何在分层数据模板中隐藏扩展器?

整行的WPF树视图项目背景

WPF 树视图展开操作

在 viewpager 中显示列表视图项的上下文菜单

如何使用 ICollectionView 过滤 wpf 树视图层次结构?