C# 如何在TreeView中仅设置顶级项目的样式?
第二天我在网上搜索,还没有找到解决办法。 以这样的元素为例:C# 如何在TreeView中仅设置顶级项目的样式?,c#,wpf,xaml,mvvm,hierarchicaldatatemplate,C#,Wpf,Xaml,Mvvm,Hierarchicaldatatemplate,第二天我在网上搜索,还没有找到解决办法。 以这样的元素为例: <TreeView ItemsSource="{Binding Types}" Width="300"> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type models:Type}" I
<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>
我将材质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
A听起来好像你并不希望每个顶级项目都像一个普通的TreeViewItem
。在这种情况下,为什么不将顶级项移到树视图
之外呢
基本上,您将拥有顶级项目的ItemsControl
,其中项目模板有点像Expander
,其中包含一个TreeView
。您可以根据自己的喜好设置顶级项目的样式
缺点是顶级项下的树将单独虚拟化,而不是作为一个整体虚拟化。也就是说,他们不会共享容器。除非你有很多顶级物品,否则这可能不是什么大问题
例如:
<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>
单击其中一个顶级项目以展开其下方的树
听起来好像你并不真的希望每个顶级项目都像一个普通的TreeViewItem
。在这种情况下,为什么不将顶级项移到树视图
之外呢
基本上,您将拥有顶级项目的ItemsControl
,其中项目模板有点像Expander
,其中包含一个TreeView
。您可以根据自己的喜好设置顶级项目的样式
缺点是顶级项下的树将单独虚拟化,而不是作为一个整体虚拟化。也就是说,他们不会共享容器。除非你有很多顶级物品,否则这可能不是什么大问题
例如:
<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>
单击其中一个顶级项目以展开其下方的树
您可以保留树状视图
并设置属性或将样式应用于顶级项目(假设您的树状视图
未嵌套在另一个树状视图
),方法是在逻辑层次结构中搜索树状视图
:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
<Setter Property="Background" Value="LightYellow" />
</DataTrigger>
对于顶级项,此绑定会向调试输出发送警告,但可以安全地忽略这些警告。这个技巧的一个更复杂的版本是创建一个可继承的附加属性TreeViewItemLevel
,该属性将在TreeView
上设置为零,并比TreeViewItem
s上的继承值多设置一个。您可以保留TreeView
并设置属性或将样式应用于通过在逻辑层次结构上搜索树视图项
,可以找到顶级项(假设您的树视图
未嵌套在另一个树视图
):
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
<Setter Property="Background" Value="LightYellow" />
</DataTrigger>
对于顶级项,此绑定会向调试输出发送警告,但可以安全地忽略这些警告。这个技巧的一个更复杂的版本是创建一个可继承的附加属性TreeViewItemLevel
,该属性将在TreeView
上设置为零,并比TreeViewItem
s上的继承值多设置一个。同时使用相对源绑定设置DataTrigger
,它将产生绑定错误
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, FallbackValue={x:Null}}" Value="{x:Null}">
另一种既不会产生错误也不会产生警告的方法是创建一个值转换器,它基本上执行与相对资源
绑定相同的操作,但也会处理错误。如果传入的任何依赖项对象的祖先类型为TreeViewItem
,则返回true
,否则返回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">
此方法适用于静态分配的treeviewitem
和ItemsSource
绑定。当使用相对源绑定设置DataTrigger
时,它将产生绑定错误
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, FallbackValue={x:Null}}" Value="{x:Null}">
另一种既不会产生错误也不会产生警告的方法是创建一个值转换器,它基本上执行与相对资源
绑定相同的操作,但也会处理错误。如果传入的任何依赖项对象的祖先类型为TreeViewItem
,则返回true
,否则返回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">
这种方法适用于静态分配的treeviewitem
和ItemsSource
bind