[英]Control template triggers cannot set value when used with StaticResource or x:Static
我遇到的奇怪问题。
当尝试将StaticResource
或x:Static
与来自ControlTemplate.Trigger
的转换器一起使用时,转换器value
始终为NULL
。
在下面的示例中,显示了不同的用法,没有任何问题:
<StackPanel Orientation="Horizontal">
<ContentControl Content="{DynamicResource Plus}"/>
<ContentControl Content="{DynamicResource Minus}"/>
<ContentControl Content="{Binding Source={StaticResource Plus}}"/>
<ContentControl Content="{Binding Source={StaticResource Minus}}"/>
<ContentControl Content="{Binding Source={StaticResource Plus}, Converter={StaticResource ToRed}}"/>
<ContentControl Content="{Binding Source={StaticResource Minus}, Converter={StaticResource ToRed}}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{x:Static icon:Icons.Plus}"/>
<ContentControl Content="{x:Static icon:Icons.Minus}"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Plus}}"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Minus}}"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Plus}, Converter={StaticResource ToRed}}"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Minus}, Converter={StaticResource ToRed}}"/>
</StackPanel>
上面的代码导致:
到目前为止一切都很好, StaticResouce
和x:Static
都可以正常工作,但是在ControlTemplate.Triggers
中使用相同的示例,然后转换器将NULL
作为VALUE
这在 VS 16.11.15 (2019) 中的 .NET 4.5、4.7.2 和 4.8 中进行了测试
重现:
我的资源.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:Key="Minus" x:Shared="False" Stretch="Uniform">
<Canvas Width="32" Height="32" Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0">
<Rectangle Width="19" Height="19" Canvas.Left="6.49999" Canvas.Top="6.5" Stretch="Fill" StrokeMiterLimit="2.75" Stroke="#FF575756"/>
<Rectangle Width="9" Height="2" Canvas.Left="11.5" Canvas.Top="15" Stretch="Fill" Fill="#FF1E5AA0"/>
</Canvas>
</Viewbox>
<Viewbox x:Key="Plus" x:Shared="False" Stretch="Uniform">
<Canvas Width="32" Height="32" Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0">
<Rectangle Width="19" Height="19" Canvas.Left="6.49999" Canvas.Top="6.5" Stretch="Fill" StrokeMiterLimit="2.75" Stroke="#FF575756"/>
<Path Width="9" Height="9" Canvas.Left="11.5" Canvas.Top="11.5" Stretch="Fill" Fill="#FF1E5AA0" Data="F1 M 20.5,15L 17,15L 17,11.5L 15,11.5L 15,15L 11.5,15L 11.5,17L 15,17L 15,20.5L 17,20.5L 17,17L 20.5,17L 20.5,15 Z "/>
</Canvas>
</Viewbox>
</ResourceDictionary>
静态类
//STATIC CLASS TO GET ICONS via x:Static
public static class Icons
{
public static UIElement Minus => GetIconByName("Minus");
public static UIElement Plus => GetIconByName("Plus");
private static UIElement GetIconByName(string name)
{
try
{
return Application.Current.FindResource(name) as UIElement;
}
catch
{
return null;
}
}
}
//STATIC CLASS TO WRITE TO THE CONSOLE TEXTBOX
public static class Console
{
public static void WriteLine(string message)
{
var console = ((MainWindow) Application.Current.MainWindow).Console;
if (console == null)
{
Debug.WriteLine(message);
return;
}
console.Text = $"{message}\n{console.Text}";
}
}
//STATIC EXTENSION CLASS FOR CHANGING COLORS
public static class Extensions
{
public static UIElement ToRed(this UIElement element)
{
if (element == null) return null;
var result = (UIElement)System.Windows.Markup.XamlReader.Parse(System.Windows.Markup.XamlWriter.Save(element));
result.ReColorAll(new SolidColorBrush(Colors.Red));
return result;
}
private static void ReColorAll(this DependencyObject element, Brush newColor)
{
if (element is null) return;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
// Retrieve child visual at specified index value.
var childVisual = VisualTreeHelper.GetChild(element, i);
switch (childVisual)
{
case Shape shape:
{
if (shape.Fill != null) shape.Fill = newColor;
if (shape.Stroke != null) shape.Stroke = newColor;
break;
}
case GeometryDrawing drawing:
{
if (drawing.Brush != null) drawing.Brush = newColor;
break;
}
}
childVisual.ReColorAll(newColor);
}
}
}
转换器类
//CONVERTER CLASS TO CONVERTER ICONS TO RED
public class ToRedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Console.WriteLine($"PARAMETER: {parameter} - VALUE: {value}");
var result = !(value is UIElement element) ? value : element.ToRed();
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
主窗口.xaml
<Window x:Class="WpfAllPurposesTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:icon="clr-namespace:WpfAllPurposesTest"
Title="InfragisticsWindow" Width="700" Height="800">
<Window.Resources>
<icon:ToRedConverter x:Key="ToRed"/>
<!--STYLE/TEMPLATE FOR TOGGLE BUTTON-->
<Style x:Key="TreeToggleButtonStyle" TargetType="ToggleButton" x:Shared="False">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid Width="20" Height="20" Background="{TemplateBinding Background}">
<ContentControl x:Name="ExpandPath" Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--STYLE/TEMPLATE FOR TREEVIEWITEM-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowToHide" Height="20"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Name="BrdBackground" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" BorderBrush="Gray" BorderThickness="0,0,0,0"/>
<!--VERTICAL LINE-->
<Border Name="VerticalLine" Grid.Row="0" Grid.RowSpan="2" Grid.Column="0">
<Path Data="M 5 1 L 5 9" StrokeThickness="0.5" Stroke="Black" Stretch="Fill" VerticalAlignment="Stretch" HorizontalAlignment="Center"/>
</Border>
<!--HORIZONTAL LINE-->
<Border Grid.Row="0" Grid.Column="0" Width="25">
<Path Data="M 12 12 L 25 12" StrokeThickness="0.5" Stroke="Black" Stretch="None"/>
</Border>
<!--EXPANDER / PLUS / MINUS ICON-->
<ToggleButton x:Name="Expander" Grid.Column="0" Grid.Row="0" Background="White" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
ClickMode="Press" Content="{Binding Source={StaticResource Plus}}" Style="{DynamicResource TreeToggleButtonStyle}" Visibility="Visible" Margin="0,0,2,0"/>
<!--TREE VIEW ITEM-->
<ContentPresenter x:Name="PART_Header" Grid.Column="1" Grid.Row="0" ContentSource="Header" HorizontalAlignment="Left"/>
<!--TREE VIEW ITEM CHILDREN HOST-->
<ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" />
<!--DUMMY BORDER TO MAKE ITEM AVAILABLE FOR MOUSE OVER AND SELECTION-->
<Border x:Name="TopBorder" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0" Background="Transparent"/>
</Grid>
<!--TRIGGERS-->
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="Expander" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsExpanded" Value="false">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="TopBorder" Property="IsMouseOver" Value="true"/>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsExpanded" Value="false"/>
</MultiTrigger.Conditions>
<Setter TargetName="Expander" Property="Content" Value="{Binding Source={StaticResource Plus}, Converter={StaticResource ToRed}, ConverterParameter=CONVERTtview1}"/>
<Setter TargetName="Expander" Property="Background" Value="LightSkyBlue"/>
<Setter TargetName="BrdBackground" Property="Background" Value="LightSkyBlue"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition SourceName="TopBorder" Property="IsMouseOver" Value="true"/>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsExpanded" Value="true"/>
</MultiTrigger.Conditions>
<Setter TargetName="Expander" Property="Content" Value="{Binding Source={StaticResource Minus}, Converter={StaticResource ToRed}, ConverterParameter=CONVERTtview2}"/>
<Setter TargetName="Expander" Property="Background" Value="LightSkyBlue"/>
<Setter TargetName="BrdBackground" Property="Background" Value="LightSkyBlue"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--STYLE/TEMPLATE FOR BUTTON-->
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel>
<ContentControl Name="Temp_Content" Width="50" Height="50" Content="{StaticResource Plus}"/>
<ContentPresenter />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Temp_Content" Property="Content" Value="{Binding Source={StaticResource Minus}, Converter={StaticResource ToRed}, ConverterParameter=CONVERTbtn}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<ContentControl Content="{DynamicResource Plus}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{DynamicResource Minus}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={StaticResource Plus}}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={StaticResource Minus}}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={StaticResource Plus}, Converter={StaticResource ToRed}}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={StaticResource Minus}, Converter={StaticResource ToRed}}" Width="50" Height="50" Margin="10"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<ContentControl Content="{x:Static icon:Icons.Plus}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{x:Static icon:Icons.Minus}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Plus}}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Minus}}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Plus}, Converter={StaticResource ToRed}}" Width="50" Height="50" Margin="10"/>
<ContentControl Content="{Binding Source={x:Static icon:Icons.Minus}, Converter={StaticResource ToRed}}" Width="50" Height="50" Margin="10"/>
</StackPanel>
<TreeView Grid.Row="2" Grid.Column="0" Width="200">
<TreeViewItem Header="Parent 1">
<TreeViewItem Header="Child 1"/>
<TreeViewItem Header="Child 2"/>
<TreeViewItem Header="Child 3"/>
<TreeViewItem Header="Child 4"/>
</TreeViewItem>
<TreeViewItem Header="Parent 2">
<TreeViewItem Header="Child 1"/>
<TreeViewItem Header="Child 2"/>
<TreeViewItem Header="Child 3"/>
<TreeViewItem Header="Child 4"/>
</TreeViewItem>
<TreeViewItem Header="Parent 3">
<TreeViewItem Header="Child 1"/>
<TreeViewItem Header="Child 2"/>
<TreeViewItem Header="Child 3"/>
<TreeViewItem Header="Child 4"/>
</TreeViewItem>
<TreeViewItem Header="Parent 4">
<TreeViewItem Header="Child 1"/>
<TreeViewItem Header="Child 2"/>
<TreeViewItem Header="Child 3"/>
<TreeViewItem Header="Child 4"/>
</TreeViewItem>
<TreeViewItem Header="Parent 5">
<TreeViewItem Header="Child 1"/>
<TreeViewItem Header="Child 2"/>
<TreeViewItem Header="Child 3"/>
<TreeViewItem Header="Child 4"/>
</TreeViewItem>
</TreeView>
<StackPanel Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center">
<Button Content="Test"/>
<Button Content="Test 1"/>
</StackPanel>
<TextBox x:Name="Console" Grid.Row="2" Grid.Column="2" IsReadOnly="True"/>
</Grid>
</Window>
主窗口.xaml.cs
public partial class MainWindow
{
public MainWindow()
{
Application.Current.Resources.MergedDictionaries.Add(
Application.LoadComponent(new Uri("WpfAllPurposesTest;component/MyResources.xaml", UriKind.Relative)
) as ResourceDictionary);
InitializeComponent();
}
}
更新:当我写这个问题时,我意识到这个问题在某种程度上是时间相关的?!?
如果我立即以某种方式使用触发器,它们会起作用,但等待几秒钟它们不会
在下面的示例中,我等待了几秒钟(查找录制的快捷方式):
当指向TreeViewItem
并且Background
变为蓝色时,图标消失。 中间的按钮也没有触发器的图标。
右侧是来自转换器的消息,显示触发了正确的触发器,但转换器没有得到值。
在下面的示例中,我使用录制快捷方式稍微快了一点:
在此示例中,父树视图项目正确显示并且图标也在更改,来自转换器的消息也显示该值不为空。
但是对于稍后触发的第二个触发器和按钮它不起作用。
有谁知道为什么会发生这种情况或如何解决?
更新
消除所有不必要的焦点和混乱。
这个问题是关于部分的:
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter ... Value="{Binding Source={StaticResource PlusRed}}"/>
</Trigger>
</ControlTemplate.Triggers>
在ControlTemplate.Trigger
中使用带有{Binding Source={StaticResource...
的Setter
时,如果您等待几秒钟,它只会在编译后短暂读取StaticResource
。 它不会读取它。
我删除了所有转换器,问题仍然存在...
结论:
经过一些评论, @BionicCode的以下评论消除了混乱。
您所指的绑定是静态的。 绑定旨在绑定到可以更改的值。 绑定将更新源袭击者的目标一个 PropertyChanged 事件。 绑定并不意味着设置对对象的引用。 您为此目的使用 StaticResource(请参阅我的回答)
为了使用 Converter,我创建了一个到 StaticResource 的绑定,这当然不是本意。
在性能方面,你做的完全错误而且太贵了。 此外,您的样式包含冗余元素,例如TreeViewItem
模板中的"TopBorder"
和错误的触发逻辑。
正确的方法是在 XAML ResourceDictionary
中定义所有资源并使用StaticResource
标记来引用它们。 不要在这种情况下使用x:Static
。
您使元素变为红色或更改其颜色的算法通常效率很低。 例如,您不应该遍历图标的可视化树来更改每个元素的Background
——即使基于元素类型(这需要实现异味类型切换)。 您当前的算法甚至需要 XAML 元素反序列化/序列化。 换句话说,整体性能很差,这在这一点上是完全没有必要的。
您可以在转换器旁边替换此逻辑,并通过正确使用数据绑定来简化它。 这也将提高性能。
我假设TreeViewItem
扩展器的期望行为是
LightSkyBlue
Red
实现触发器时,您必须为模板化元素提供默认状态。
由于布尔变量的默认值以及扩展器的ToggleButton.IsChecked
属性为false
,因此您应该根据折叠状态设计模板。 然后定义触发器以在状态更改为展开时更改元素的外观。 这减少了触发器的数量并提高了可读性。
定义您的图标以允许动态着色,即填充和描边。 这样我们就可以通过数据绑定来设置这些属性。
以下示例期望绘图托管在ContentControl
(或派生类型,如Button
)中。 它使用ContentControl.Background
为图标的背景(在本例中为Canvas.Background
)着色,并使用ContentControl.Foreground
为实际图标绘图的笔划着色。
为了实现这种行为,我们使用Bindig.RelativeSource
绑定到ContentControl
(稍后将在不同范围内添加的父级):
<Viewbox x:Key="MinusIcon"
x:Shared="False"
Stretch="Uniform">
<Canvas Background="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Background}"
Width="32"
Height="32"
Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0">
<Rectangle Width="19"
Height="19"
Canvas.Left="6.49999"
Canvas.Top="6.5"
Stretch="Fill"
StrokeMiterLimit="2.75"
Stroke="#FF575756" /> <!-- Static border color (gray) -->
<Rectangle Fill="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Foreground}"
Width="9"
Height="2"
Canvas.Left="11.5"
Canvas.Top="15"
Stretch="Fill" />
</Canvas>
</Viewbox>
<Viewbox x:Key="PlusIcon"
x:Shared="False"
Stretch="Uniform">
<Canvas Background="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Background}"
Width="32"
Height="32"
Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0">
<Rectangle Width="19"
Height="19"
Canvas.Left="6.49999"
Canvas.Top="6.5"
Stretch="Fill"
StrokeMiterLimit="2.75"
Stroke="#FF575756" /> <!-- Static border color (gray) -->
<Path Fill="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Foreground}"
Width="9"
Height="9"
Canvas.Left="11.5"
Canvas.Top="11.5"
Stretch="Fill"
Data="F1 M 20.5,15L 17,15L 17,11.5L 15,11.5L 15,15L 11.5,15L 11.5,17L 15,17L 15,20.5L 17,20.5L 17,17L 20.5,17L 20.5,15 Z " />
</Canvas>
</Viewbox>
当您想使用动态图标时,您可以将它们托管在任何ContentControl
中。 您使用StaticResource
标记来引用图标资源。 你直接引用它(没有Binding
到资源)。 例如
<Button Content="{StaticResource PlusIcon}" />
同样,您定义默认状态(如果是未单击按钮)并定义在状态更改时修改元素的触发器。 强烈建议使用VisualStateManager
而不是触发器。
为了使答案在技术上尽可能简单,以下示例使用触发器。 它们会更改图标的颜色和图标本身(根据您的要求):
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel>
<ContentControl x:Name="IconHost"
Foreground="LightSkyBlue"
Background="Transparent"
Content="{StaticResource PlusIcon}"
Width="50"
Height="50" />
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="IconHost"
Property="Content"
Value="{StaticResource MinusIcon}" />
<Setter TargetName="IconHost"
Property="Foreground"
Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TreeViewItem
的ToggleButton
中使用动态图标您可以使用第 2 步中的图标,将它们托管在ContentControl
中。
以下片段显示了如何将图标与扩展器ToggleButton
一起使用。 您可以将ToggleButton.Content
的设置移动到Style
:
<ToggleButton x:Name="Expander"
Grid.Column="0"
Grid.Row="0"
Content="{StaticResource PlusIcon}"
Background="{TemplateBinding Background}"
Foreground="LightSkyBlue"
IsChecked="{TemplateBinding IsExpanded}"
ClickMode="Press"
Style="{DynamicResource TreeToggleButtonStyle}"
Visibility="Visible"
Margin="0,0,2,0" /> <!-- Consider to use {StaticResource TreeToggleButtonStyle} when possible -->
以下示例是TreeViewItem
样式的改进版本。 它修复了不正确的触发逻辑并突出显示行为。
完整的逻辑完全在 XAML 中实现。 它使用Step 1
中定义的动态图标和数据绑定来使绑定转换器和扩展方法过时。
改进了布局,例如删除了多余的"TopBorder"
,所有元素都绑定到模板化父级的TreeViewItem.Background
属性以获取当前背景突出显示颜色(以减少触发器设置器)。
然后完整的模板现在基于默认状态,即折叠的树节点。 Visibility
和Background
等所有属性都进行了相应配置。
完整的TreeViewItem
样式如下所示:
<Window>
<Window.Resources>
<!-- Minus icon -->
<Viewbox x:Key="MinusIcon"
x:Shared="False"
Stretch="Uniform">
<Canvas Width="32"
Height="32"
Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0"
Background="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Background}">
<Rectangle Width="19"
Height="19"
Canvas.Left="6.49999"
Canvas.Top="6.5"
Stretch="Fill"
StrokeMiterLimit="2.75"
Stroke="#FF575756" />
<Rectangle Width="9"
Height="2"
Canvas.Left="11.5"
Canvas.Top="15"
Stretch="Fill"
Fill="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Foreground}" />
</Canvas>
</Viewbox>
<!-- Plus icon -->
<Viewbox x:Key="PlusIcon"
x:Shared="False"
Stretch="Uniform">
<Canvas Width="32"
Height="32"
Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0"
Background="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Background}">
<Rectangle Width="19"
Height="19"
Canvas.Left="6.49999"
Canvas.Top="6.5"
Stretch="Fill"
StrokeMiterLimit="2.75"
Stroke="#FF575756" />
<Path Width="9"
Height="9"
Canvas.Left="11.5"
Canvas.Top="11.5"
Stretch="Fill"
Fill="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=Foreground}"
Data="F1 M 20.5,15L 17,15L 17,11.5L 15,11.5L 15,15L 11.5,15L 11.5,17L 15,17L 15,20.5L 17,20.5L 17,17L 20.5,17L 20.5,15 Z " />
</Canvas>
</Viewbox>
<!-- Button style -->
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel>
<ContentControl x:Name="IconHost"
Width="50"
Height="50"
Foreground="LightSkyBlue"
Background="Transparent"
Content="{StaticResource Plus}" />
<ContentPresenter />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="IconHost"
Property="Content"
Value="{StaticResource Minus}" />
<Setter TargetName="IconHost"
Property="Foreground"
Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<TreeView>
<!-- TreeViewItem style -->
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Background"
Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowToHide"
Height="20" />
<RowDefinition />
</Grid.RowDefinitions>
<Border Name="BrdBackground"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Background="{TemplateBinding Background}"
BorderBrush="Gray"
BorderThickness="0,0,0,0" />
<!--VERTICAL LINE-->
<Border Name="VerticalLine"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0">
<Path Data="M 5 1 L 5 9"
StrokeThickness="0.5"
Stroke="Black"
Stretch="Fill"
VerticalAlignment="Stretch"
HorizontalAlignment="Center" />
</Border>
<!--HORIZONTAL LINE-->
<Border Grid.Row="0"
Grid.Column="0"
Width="25">
<Path Data="M 12 12 L 25 12"
StrokeThickness="0.5"
Stroke="Black"
Stretch="None" />
</Border>
<!--EXPANDER / PLUS / MINUS ICON-->
<ToggleButton x:Name="Expander"
Grid.Column="0"
Grid.Row="0"
Content="{StaticResource PlusIcon}"
Background="{TemplateBinding Background}"
Foreground="LightSkyBlue"
IsChecked="{TemplateBinding IsExpanded}"
ClickMode="Press"
Style="{DynamicResource TreeToggleButtonStyle}"
Visibility="Visible"
Margin="0,0,2,0" /> <!-- Consider to use {StaticResource TreeToggleButtonStyle} when possible (to improve performance) -->
<!--TREE VIEW ITEM HEADER -->
<ContentPresenter x:Name="PART_Header"
Grid.Column="1"
Grid.Row="0"
ContentSource="Header"
HorizontalAlignment="Left" />
<!--TREE VIEW ITEM CHILDREN HOST-->
<ItemsPresenter x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems"
Value="false">
<Setter TargetName="Expander"
Property="Visibility"
Value="Collapsed" />
</Trigger>
<Trigger Property="IsExpanded"
Value="True">
<Setter TargetName="ItemsHost"
Property="Visibility"
Value="Visible" />
<Setter TargetName="Expander"
Property="Content"
Value="{StaticResource MinusIcon}" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="LightSkyBlue" />
<Setter TargetName="Expander"
Property="Foreground"
Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Window>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.