在WPF中的整个TreeViewItem行上显示ContextMenu

时间:2017-02-18 12:14:57

标签: wpf xaml templates contextmenu treeviewitem

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

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

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

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

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

这似乎需要将上下文菜单附加到ControlTemplate中的元素,并将TemplateBinding附加到HierarchicalDataTemplate中定义的内容,但我无法弄清楚如何执行此操作。

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

1 个答案:

答案 0 :(得分:1)

(编辑)您需要摆脱Grid样式中TreeViewItem的两列:

<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部分中的边距:

<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();
    }
}

如果转换器参数为1,则会在项目的深度上添加一个点。我将它用于 Header 部分。

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