【问题标题】:Show ContextMenu on whole TreeViewItem line in WPF在 WPF 中的整个 TreeViewItem 行上显示 ContextMenu
【发布时间】:2017-02-18 12:14:57
【问题描述】:

我有一个具有异构节点类型的 TreeView。每个节点类型都在 XAML 中使用 HierarchicalDataTemplate 进行配置。

一些节点有一个ContextMenu,这取决于节点的类型。 ContextMenus 在 XAML 中定义为静态资源,并附加到 HierarchicalDataTemplate 中的 DockPanel。

此外,我在下面的 StackOverflow 问题 https://stackoverflow.com/a/672123/1626109 中使用了 Bendewey 描述的 TreeViewItem 的 ControlTemplate。定义此 ControlTemplate 以便在选中时突出显示完整的 TreeViewItem。 左键单击该行的任何部分,选择该项目。

相比之下,在 HierarchicalDataTemplate 中定义的上下文菜单仅适用于该行的右侧部分。

我正在寻找一种配置 ContextMenus 的方法,以便它们也可以在整行中使用。

这似乎需要将上下文菜单附加到 ControlTemplate 中的元素,并使用 TemplateBinding 到 HierarchicalDataTemplate 中定义的内容,但我不知道该怎么做。

顺便说一句,Visual Studio 中的解决方案资源管理器正是这种行为。上下文菜单取决于节点类型,它在整个项目中都可用,包括展开/折叠按钮的左侧。

【问题讨论】:

    标签: wpf xaml templates contextmenu treeviewitem


    【解决方案1】:

    (已编辑)您需要在TreeViewItem 样式中去掉Grid 的两列:

    <Window.Resources>
        <local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
        <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
        <Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Grid Width="15" Height="13"
          Background="Transparent">
                            <Path x:Name="ExpandPath"
            HorizontalAlignment="Left" 
            VerticalAlignment="Center" 
            Margin="1,1,1,1"
            Fill="{StaticResource GlyphBrush}"
            Data="M 4 0 L 8 4 L 4 8 Z"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked"
               Value="True">
                                <Setter Property="Data"
                TargetName="ExpandPath"
                Value="M 0 4 L 8 4 L 4 8 Z"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="TreeViewItemFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Border>
                            <Rectangle Margin="0,0,0,0"
                 StrokeThickness="5"
                 Stroke="Black"
                 StrokeDashArray="1 2"
                 Opacity="0"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="{x:Type TreeViewItem}"
     TargetType="{x:Type TreeViewItem}">
            <Setter Property="Background"
      Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment"
      Value="{Binding Path=HorizontalContentAlignment,
              RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment"
      Value="{Binding Path=VerticalContentAlignment,
              RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Padding"
      Value="1,0,0,0"/>
            <Setter Property="Foreground"
      Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="FocusVisualStyle"
      Value="{StaticResource TreeViewItemFocusVisual}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <ControlTemplate.Resources>
                            <local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" /> 
                        </ControlTemplate.Resources>
                        <StackPanel>
                            <Border Name="Bd"
              Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              Padding="{TemplateBinding Padding}">
                                <Grid>
                                    <ToggleButton Panel.ZIndex="2" x:Name="Expander"
                                                  HorizontalAlignment="Left"
                  Style="{StaticResource ExpandCollapseToggleStyle}"
                  IsChecked="{Binding Path=IsExpanded,
                              RelativeSource={RelativeSource TemplatedParent}}"
                  ClickMode="Press" 
                                                  Margin="{Binding  Converter={StaticResource lengthConverter}, ConverterParameter=0,
                              RelativeSource={RelativeSource TemplatedParent}}"/>
    
                                    <ContentPresenter x:Name="PART_Header" ContentSource="Header"
                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
                                    </ContentPresenter>
                                </Grid>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" />
                        </StackPanel>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded"
               Value="false">
                                <Setter TargetName="ItemsHost"
                Property="Visibility"
                Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems"
               Value="false">
                                <Setter TargetName="Expander"
                Property="Visibility"
                Value="Hidden"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="HasHeader"
                     Value="false"/>
                                    <Condition Property="Width"
                     Value="Auto"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_Header"
                Property="MinWidth"
                Value="75"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="HasHeader"
                     Value="false"/>
                                    <Condition Property="Height"
                     Value="Auto"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_Header"
                Property="MinHeight"
                Value="19"/>
                            </MultiTrigger>
                            <Trigger Property="IsSelected"
               Value="true">
                                <Setter TargetName="Bd"
                Property="Background"
                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected"
                     Value="true"/>
                                    <Condition Property="IsSelectionActive"
                     Value="false"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Bd"
                Property="Background"
                Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled"
               Value="false">
                                <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    

    但是,这会扭曲 Header-Items 的排列。因此,需要调整HierarchicalDataTemplate部分的Margin:

    <TreeView Margin="50" HorizontalContentAlignment="Stretch" DataContext="{Binding}" ItemsSource="{Binding Models}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:Model}" ItemsSource="{Binding Models}">
                <Border Background="Transparent">
                    <TextBlock Margin="{Binding  Converter={StaticResource lengthConverter}, ConverterParameter=1,
                                                                     RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TreeViewItem}}" 
                               Text="{Binding Name}" />
                    <Border.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="dddd"/>
                        </ContextMenu>
                    </Border.ContextMenu>
                </Border>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    

    请注意,您应该调整转换器以考虑所需的额外边距:

    public class LeftMarginMultiplierConverter : IValueConverter
    {
        public double Length { get; set; } 
    
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var item = value as TreeViewItem;
            if (item == null)
                return new Thickness(0);
            int extra = int.Parse(parameter.ToString());
            var t = item.GetDepth();
            return new Thickness(Length * (item.GetDepth() + extra), 0, 0, 0);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new System.NotImplementedException();
        }
    }
    

    如果converter参数为1,它会在item的深度上加一个点。我将它用于 Header 部分。

    另一种方法可能是将DataTemplate 添加到TreeViewItem StyleContentPresenter。虽然我不知道如何绑定 ContextMenu,但我更喜欢这个。

    【讨论】:

    • 这不是问题。 TreeViewItem 的 ControlTemplate 根据树的深度定义边距,然后是展开/折叠按钮,然后是 DataTemplate 所在的 ContentPresenter。我想要整行的上下文菜单,所以它需要在 ControlTemplate 中定义(在边框上、网格上或一些新元素上),但要绑定到 HierarchicalDataTemplate 中的定义。
    • 谢谢,这行得通。我对将缩进的责任转移到 DataTemplates 和 HierarchicalDataTemplates 有一点不好的感觉。我的直觉反应说它不属于那里。 (但是,您可以争辩说 ContextMenu 也不属于该行的那一部分。)
    猜你喜欢
    • 2010-10-14
    • 2014-08-25
    • 1970-01-01
    • 2013-07-01
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    • 2011-03-03
    • 1970-01-01
    相关资源
    最近更新 更多