繁体   English   中英

与 StaticResource 或 x:Static 一起使用时,控件模板触发器无法设置值

[英]Control template triggers cannot set value when used with StaticResource or x:Static

我遇到的奇怪问题。

当尝试将StaticResourcex: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>

上面的代码导致:

在此处输入图像描述

到目前为止一切都很好, StaticResoucex: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 ,因此您应该根据折叠状态设计模板。 然后定义触发器以在状态更改为展开时更改元素的外观。 这减少了触发器的数量并提高了可读性。

第 1 步:实现动态图标

定义您的图标以允许动态着色,即填充和描边。 这样我们就可以通过数据绑定来设置这些属性。
以下示例期望绘图托管在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>

第 2 步:在按钮中使用动态图标

当您想使用动态图标时,您可以将它们托管在任何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>

第三步:在TreeViewItemToggleButton中使用动态图标

您可以使用第 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 -->

第 4 步:将所有内容放在一起

以下示例是TreeViewItem样式的改进版本。 它修复了不正确的触发逻辑并突出显示行为。

完整的逻辑完全在 XAML 中实现。 它使用Step 1中定义的动态图标和数据绑定来使绑定转换器和扩展方法过时。

改进了布局,例如删除了多余的"TopBorder" ,所有元素都绑定到模板化父级的TreeViewItem.Background属性以获取当前背景突出显示颜色(以减少触发器设置器)。

然后完整的模板现在基于默认状态,即折叠的树节点。 VisibilityBackground等所有属性都进行了相应配置。

完整的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM