如何在XAML / WPF中仅设置树视图中的顶级样式

时间:2018-01-31 18:12:00

标签: c# wpf xaml mvvm hierarchicaldatatemplate

第二天我在网上搜索并没有找到解决方案。 XAML,MVVM,WPF 采取这样的元素:

        <TreeView ItemsSource="{Binding Types}" Width="300">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type models:Type}"
                                      ItemsSource="{Binding SubTypes}">
                    <TextBlock Text="{Binding Name}"/>
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate DataType="{x:Type SubType}">
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>

我使用Material nuget库作为基本样式 现在我需要禁用悬停,在第一级项目上选择等,只允许选择/悬停子项目。

但我似乎发现的一切都是关于每个项目的内容的样式或全局样式。

A <- remove selection/hover (pref single click too but that's another topic)
  A1 <- maintain original style, hover and select
  A2 <- maintain original style, hover and select
  A3 <- maintain original style, hover and select
B <- remove selection/hover (pref single click too but that's another topic)
  B1 <- maintain original style, hover and select
  B2 <- maintain original style, hover and select
  B3 <- maintain original style, hover and select

3 个答案:

答案 0 :(得分:2)

听起来你真的不希望每个顶级项目都像普通TreeViewItem那样行事。在这种情况下,为什么不将TreeView的顶级项移到之外?

基本上,您有ItemsControl个顶级项目,其中项目模板的行为有点像Expander,其中包含TreeView个项目。您可以根据自己的喜好设置顶级项目的样式。

缺点是顶级项目下的树木将单独虚拟化,而不是整体。也就是说,他们不会共享容器。除非您有 ton 的顶级商品,否则这可能不会有什么大不了的。

示例:

<ItemsControl xmlns:s="clr-namespace:System;assembly=mscorlib"
              ItemsSource="{Binding Types}">
  <ItemsControl.Resources>
    <ControlTemplate x:Key="ExpanderButtonTemplate" TargetType="ToggleButton">
      <Border Background="Transparent" Padding="3,2">
        <ContentPresenter />
      </Border>
    </ControlTemplate>
    <Style TargetType="Expander">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Expander">
            <DockPanel LastChildFill="True">
              <ToggleButton DockPanel.Dock="Top"
                            IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                            Template="{StaticResource ExpanderButtonTemplate}">
                <ContentPresenter ContentSource="Header" />
              </ToggleButton>
              <Border>
                <ContentPresenter x:Name="contentSite" Visibility="Collapsed" />
              </Border>
            </DockPanel>
            <ControlTemplate.Triggers>
              <Trigger Property="IsExpanded" Value="True">
                <Setter TargetName="contentSite" Property="Visibility" Value="Visible" />
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </ItemsControl.Resources>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Expander Header="{Binding Name}">
        <TreeView ItemsSource="{Binding SubTypes}" BorderThickness="0" Padding="0">
          <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type models:Type}"
                                      ItemsSource="{Binding SubTypes}">
              <TextBlock Text="{Binding Name}"/>
              <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate DataType="{x:Type models:SubType}">
                  <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
              </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>
          </TreeView.ItemTemplate>
        </TreeView>
      </Expander>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

点击其中一个顶级项目以展开其下方的树。

Screenshot

答案 1 :(得分:0)

您可以通过搜索逻辑来保留TreeView并设置属性或将样式应用于您的顶级项(假设TreeView没有嵌套在另一个TreeView中) TreeViewItem的层次结构:

<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
  <Setter Property="Background" Value="LightYellow" />
</DataTrigger>

对于顶级项目,此绑定将垃圾邮件警告发送给调试输出,但可以放心地忽略它们。此技巧的更复杂的版本是创建一个可继承的附加属性TreeViewItemLevel,该属性在TreeView上设置为零,并在TreeViewItem s上设置为一个继承的值。< / p>

答案 2 :(得分:0)

在设置具有相对源绑定的DataTrigger的同时,它将产生绑定错误

<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">

通过设置FallbackValue中的x:Null,您不会收到任何绑定错误,但会收到警告

<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, FallbackValue={x:Null}}" Value="{x:Null}">

一种既不会产生错误也不会发出警告的替代方法是创建一个值转换器,该值转换器实际上执行相同的RelativeSource绑定,但也会处理错误。如果传入的任何依赖对象的祖先类型为true,则返回TreeViewItem,否则返回false

public class IsRootTreeViewItemConverter : IValueConverter
{
   public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
      return value is null ? Binding.DoNothing : !HasTreeViewItemAncestor((DependencyObject)value);
   }

   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
      throw new InvalidOperationException();
   }

   private static bool HasTreeViewItemAncestor(DependencyObject child)
   {
      var parent = VisualTreeHelper.GetParent(child);

      return parent switch
      {
         null => false,
         TreeViewItem _ => true,
         _ => HasTreeViewItemAncestor(parent)
      };
   }
}

在您的TreeViewItem样式触发器中,可以通过使用Self绑定到项目本身来使用转换器。

<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsRootTreeViewItemConverter}}" Value="True">

此方法适用于静态分配的TreeViewItemsItemsSource绑定。