2017-02-18 11 views
0

У меня есть TreeView с гетерогенными типами узлов. Каждый тип узла настраивается в XAML с использованием HierarchicalDataTemplate.Показать ContextMenu на всей строке TreeViewItem в WPF

Некоторые узлы имеют ContextMenu, который зависит от типа узла. ContextMenus определяются как статические ресурсы в XAML и прикрепляются к DockPanel в HierarchicalDataTemplate.

Кроме того, я использую ControlTemplate для TreeViewItem, описанный bendewey в следующем вопросе StackOverflow https://stackoverflow.com/a/672123/1626109. Этот ControlTemplate определяется так, чтобы весь TreeViewItem был выделен, когда он выбран. Щелкните левой кнопкой мыши по любой части линии, выберите элемент.

Напротив, контекстные меню, которые определены в HierarchicalDataTemplate, работают только в правой части строки.

Я ищу способ настройки ContextMenus, чтобы они также были доступны в полной строке.

Это, похоже, требует подключения контекстного меню к элементу в ControlTemplate, а TemplateBinding - к определенному в HierarchicalDataTemplate, но я не могу понять, как это сделать.

Кстати, исследователь решений в Visual Studio имеет именно такое поведение. Контекстное меню зависит от типа узла, и оно доступно для всего элемента, включая левую часть кнопки expand/collapse.

ответ

1

(Edited) Вы должны избавиться от двух столбцов 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> 

Однако, это приведет к искажению заголовков 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 , он добавит одну точку к глубине элемента. Я использую его для Заголовок часть.

Другим подходом может быть добавление DataTemplate к ContentPresenter из TreeViewItemStyle. Хотя я не знаю, как вы можете привязать ContextMenu, я предпочитаю этот.

+0

Это не проблема. ControlTemplate для TreeViewItem определяет маржу в соответствии с глубиной дерева, затем кнопку развернуть/свернуть, а затем появится ContentPresenter, где живет DataTemplate. Я хочу контекстное меню на всей строке, поэтому его необходимо определить в ControlTemplate (на границе, в сетке или на каком-то новом элементе), но привязана к определению в HierarchicalDataTemplate. –

+0

Все в порядке. См. Редактирование. – Ron

+0

Спасибо, что работает. У меня немного плохое чувство о переносе ответственности за отступ вниз в DataTemplates и HierarchicalDataTemplates. Моя реакция кишки говорит, что она там не принадлежит. (Тем не менее, вы можете утверждать, что ContextMenu не принадлежит к этой части строки.) –

Смежные вопросы