我有一个带有异构节点类型的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中的解决方案资源管理器正是这种行为。上下文菜单取决于节点类型,并且可在完整项目上使用,包括展开/折叠按钮的左侧。
答案 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,但我更喜欢这个。