簡體   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