【问题标题】:Settings triggers on 'self' properties passed down from parent templates (WPF)从父模板 (WPF) 传递的“自我”属性的设置触发器
【发布时间】:2011-04-08 21:31:19
【问题描述】:

我目前正在使用一种相当复杂的方法来模板化/样式化 WPF TreeView 控件。这包括 3 种样式,一种用于 TreeView,一种用于 TreeViewItem,一种用于 ToggleButton。在我认为在 TreeView 上公开一些自定义依赖属性是一个很酷的主意之前,我的一切工作都很好。其中一些包括 ItemBackgroundBrush、ItemHighlightBrush 和 ItemSelectedBrush。因此,您可以在使用 TreeView 标记时定义或更改每个值,而不必不断更改模板(存储在资源字典中)。我的所有画笔都会影响 ToggleButton,恰当地命名为 TreeViewToggle,并通过绑定向下传递到可视树。原因是 TreeViewitem 的 IsMouseOver 是真的,即使鼠标指针位于项目的一个子项上,我想这是因为它的 ItemsPanel 在技术上仍然是项目的一部分。为了超越这一点,我处理了 TreeViewToggle 的 IsMouseOver 触发器,因此将悬停/选择画笔应用于适当的标题。我想我会在下面发布代码,让您了解我在做什么,但我警告您它变得非常大,哈哈。一切正常,直到 TreeViewToggleStyle 的 IsMouseOver 触发器。 LayoutRoot) 的背景属性...(当前的设置器代码只是不同绑定语法的众多尝试之一。我已经尝试了很多我在网上看到的自绑定示例,但它们似乎都不起作用: (同样,对于这个巨大的帖子感到抱歉,但我不确定哪些信息是相关的,哪些信息不相关。 提前致谢, 瑞恩。

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication2">

<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>

<Style x:Key="TreeViewToggleStyle" TargetType="{x:Type my:TreeViewToggle}">
    <Setter Property="Focusable" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type my:TreeViewToggle}">
                <Border x:Name="LayoutBorder" 
                    CornerRadius="2" 
                    Background="{TemplateBinding Background}" 
                    BorderBrush="{TemplateBinding BorderBrush}" 
                    BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Path x:Name="Expander" Stroke="#FF989898" Data="{StaticResource TreeArrow}">
                            <Path.RenderTransform>
                                <RotateTransform Angle="135" CenterY="6" CenterX="3"/>
                            </Path.RenderTransform>
                        </Path>
                        <ContentPresenter x:Name="PART_Content" Margin="12,0,0,0" />
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasItems" Value="False">
                        <Setter TargetName="Expander" Property="Visibility" Value="Collapsed"/>
                        <Setter TargetName="PART_Content" Property="Margin" Value="0"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="{Binding Path=HighlightBrush, RelativeSource={RelativeSource Mode=Self}}"/>
                        <Setter Property="BorderBrush" Value="{Binding Path=HighlightBorderBrush}"/>
                        <Setter Property="BorderThickness" Value="{Binding Path=HighlightBorderThickness}"/>
                    </Trigger>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="RenderTransform" TargetName="Expander">
                            <Setter.Value>
                                <RotateTransform Angle="180" CenterY="5" CenterX="4"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsChecked" Value="False"/>
                            <Condition Property="IsMouseOver" Value="True"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Stroke" TargetName="Expander" Value="#FF1BBBFA"/>
                        <Setter Property="Fill" TargetName="Expander" Value="Transparent"/>
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsChecked" Value="True"/>
                            <Condition Property="IsMouseOver" Value="True"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Stroke" TargetName="Expander" Value="#FF1BBBFA"/>
                        <Setter Property="Fill" TargetName="Expander" Value="#FF1BBBFA"/>
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="SolaceTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TreeViewItem}">
                <Grid x:Name="LayoutRoot">
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <my:TreeViewToggle x:Name="PART_Expander"
                        ClickMode="Press" 
                        ContentTemplate="{TemplateBinding HeaderTemplate}" 
                        Content="{TemplateBinding Header}" 
                        HasItems="{Binding HasItems, RelativeSource={RelativeSource TemplatedParent}}" 
                        IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" 
                        IsSelected="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}}" 
                        Style="{DynamicResource TreeViewToggleStyle}" 
                        Background="{Binding ItemBackground, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}" 
                        BorderBrush="{Binding ItemBorderBrush, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}" 
                        BorderThickness="{Binding ItemBorderThickness, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}" 
                        HighlightBrush="{Binding ItemHighlightBackground, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}" 
                        HighlightBorderBrush="{Binding ItemHighlightBorderBrush, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}"
                        HighlightBorderThickness="{Binding ItemHighlightBorderThickness, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}"
                        SelectedBrush="{Binding ItemSelectedBackground, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}" 
                        SelectedBorderBrush="{Binding ItemSelectedBorderBrush, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}"
                        SelectedBorderThickness="{Binding ItemSelectedBorderThickness, RelativeSource={RelativeSource AncestorType={x:Type my:TreeViewEx}}}"/>
                    <ItemsPresenter x:Name="PART_Items" Grid.Row="1" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasItems" Value="False">
                        <Setter TargetName="PART_Items" Property="Visibility" Value="Collapsed"/>
                    </Trigger>
                    <Trigger Property="IsExpanded" Value="False">
                        <Setter TargetName="PART_Items" Property="Visibility" Value="Collapsed"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<!-- TreeView style omitted as its very simple -->
</ResourceDictionary>

我还定义了 2 个类,并暴露了它们的依赖属性:

public class TreeViewEx : TreeView
{
    public Brush ItemBorderBrush
    {
        get { return (Brush)base.GetValue(ItemBorderBrushProperty); }
        set { base.SetValue(ItemBorderBrushProperty, value); }
    }

    public Brush ItemBackground
    {
        get { return (Brush)base.GetValue(ItemBackgroundProperty); }
        set { base.SetValue(ItemBackgroundProperty, value); }
    }

    public Double ItemBorderThickness 
    {
        get { return (Double)base.GetValue(ItemBorderThicknessProperty); }
        set { base.SetValue(ItemBorderThicknessProperty, value); }
    }

    public Brush ItemHighlightBorderBrush
    {
        get { return (Brush)base.GetValue(ItemBorderBrushProperty); }
        set { base.SetValue(ItemBorderBrushProperty, value); }
    }

    public Brush ItemHighlightBackground
    {
        get { return (Brush)base.GetValue(ItemHighlightBackgroundProperty); }
        set { base.SetValue(ItemHighlightBackgroundProperty, value); }
    }

    public Double ItemHighlightBorderThickness
    {
        get { return (Double)base.GetValue(ItemBorderThicknessProperty); }
        set { base.SetValue(ItemBorderThicknessProperty, value); }
    }

    public Brush ItemSelectedBorderBrush
    {
        get { return (Brush)base.GetValue(ItemBorderBrushProperty); }
        set { base.SetValue(ItemBorderBrushProperty, value); }
    }

    public Brush ItemSelectedBackground
    {
        get { return (Brush)base.GetValue(ItemSelectedBackgroundProperty); }
        set { base.SetValue(ItemSelectedBackgroundProperty, value); }
    }

    public Double ItemSelectedBorderThickness
    {
        get { return (Double)base.GetValue(ItemBorderThicknessProperty); }
        set { base.SetValue(ItemBorderThicknessProperty, value); }
    }

    public static readonly DependencyProperty ItemBorderBrushProperty =
        DependencyProperty.Register("ItemBorderBrush",
        typeof(Brush),
        typeof(TreeViewEx),
        new PropertyMetadata(null));

    public static readonly DependencyProperty ItemBorderThicknessProperty =
        DependencyProperty.Register("ItemBorderThickness",
        typeof(Double),
        typeof(TreeViewEx),
        new PropertyMetadata(1D));

    public static readonly DependencyProperty ItemBackgroundProperty =
        DependencyProperty.Register("ItemBackground",
        typeof(Brush),
        typeof(TreeViewEx),
        new PropertyMetadata(null));

    public static readonly DependencyProperty ItemHighlightBorderBrushProperty =
        DependencyProperty.Register("ItemHighlightBorderBrush",
        typeof(Brush),
        typeof(TreeViewEx),
        new PropertyMetadata(null));

    public static readonly DependencyProperty ItemHighlightBorderThicknessProperty =
        DependencyProperty.Register("ItemHighlightBorderThickness",
        typeof(Double),
        typeof(TreeViewEx),
        new PropertyMetadata(1D));

    public static readonly DependencyProperty ItemHighlightBackgroundProperty =
        DependencyProperty.Register("ItemHighlightBackground",
        typeof(Brush),
        typeof(TreeViewEx),
        new PropertyMetadata(null));

    public static readonly DependencyProperty ItemSelectedBorderBrushProperty =
        DependencyProperty.Register("ItemSelectedBorderBrush",
        typeof(Brush),
        typeof(TreeViewEx),
        new PropertyMetadata(null));

    public static readonly DependencyProperty ItemSelectedBorderThicknessProperty =
        DependencyProperty.Register("ItemSelectedBorderThickness",
        typeof(Double),
        typeof(TreeViewEx),
        new PropertyMetadata(1D));

    public static readonly DependencyProperty ItemSelectedBackgroundProperty =
        DependencyProperty.Register("ItemSelectedBackground",
        typeof(Brush),
        typeof(TreeViewEx),
        new PropertyMetadata(SystemColors.HighlightBrush));
}

//this class was created to be used with the treeview control template
//it enables the passing of relevant information down the visual tree to sub-templates
//that perform extra layout duties based on data that the element doesn't natively support
public class TreeViewToggle : ToggleButton
{
    public bool HasItems
    {
        get { return (bool)base.GetValue(HasItemsProperty); }
        set { base.SetValue(HasItemsProperty, value); }
    }

    public bool IsSelected
    {
        get { return (bool)base.GetValue(IsSelectedProperty); }
        set { base.SetValue(IsSelectedProperty, value); }
    }

    public Brush HighlightBrush 
    {
        get { return (Brush)base.GetValue(HighlightBrushProperty); }
        set { base.SetValue(HighlightBrushProperty, value); }
    }

    public Brush HighlightBorderBrush 
    {
        get { return (Brush)base.GetValue(HighlightBorderBrushProperty); }
        set { base.SetValue(HighlightBorderBrushProperty, value); }
    }

    public Double HighlightBorderThickness 
    {
        get { return (Double)base.GetValue(HighlightBorderThicknessProperty); }
        set { base.SetValue(HighlightBorderThicknessProperty, value); }
    }

    public Brush SelectedBrush 
    {
        get { return (Brush)base.GetValue(SelectedBrushProperty); }
        set { base.SetValue(SelectedBrushProperty, value); }
    }

    public Brush SelectedBorderBrush 
    {
        get { return (Brush)base.GetValue(SelectedBorderBrushProperty); }
        set { base.SetValue(SelectedBorderBrushProperty, value); }
    }

    public Double SelectedBorderThickness 
    {
        get { return (Double)base.GetValue(SelectedBorderThicknessProperty); }
        set { base.SetValue(SelectedBorderThicknessProperty, value); }
    }

    public static readonly DependencyProperty HasItemsProperty = 
        DependencyProperty.Register("HasItems",
        typeof(bool),
        typeof(TreeViewToggle));

    public static readonly DependencyProperty IsSelectedProperty = 
        DependencyProperty.Register("IsSelected",
        typeof(bool),
        typeof(TreeViewToggle));

    public static readonly DependencyProperty HighlightBrushProperty =
        DependencyProperty.Register("HighlightBrush",
        typeof(Brush),
        typeof(TreeViewToggle),
        new PropertyMetadata(null));

    public static readonly DependencyProperty HighlightBorderBrushProperty =
        DependencyProperty.Register("HighlightBorderBrush",
        typeof(Brush),
        typeof(TreeViewToggle),
        new PropertyMetadata(null));

    public static readonly DependencyProperty HighlightBorderThicknessProperty =
        DependencyProperty.Register("HighlightBorderThickness",
        typeof(Double),
        typeof(TreeViewToggle),
        new PropertyMetadata(1D));

    public static readonly DependencyProperty SelectedBrushProperty =
        DependencyProperty.Register("SelectedBrush",
        typeof(Brush),
        typeof(TreeViewToggle),
        new PropertyMetadata(null));

    public static readonly DependencyProperty SelectedBorderBrushProperty =
        DependencyProperty.Register("SelectedBorderBrush",
        typeof(Brush),
        typeof(TreeViewToggle),
        new PropertyMetadata(null));

    public static readonly DependencyProperty SelectedBorderThicknessProperty =
        DependencyProperty.Register("SelectedBorderThickness",
        typeof(Double),
        typeof(TreeViewToggle),
        new PropertyMetadata(1D));
}

【问题讨论】:

    标签: c# .net wpf xaml c#-4.0


    【解决方案1】:

    您在 SolaceTreeViewItemStyle 的 ControlTemplate 中设置 TreeViewToggle 的 Background 属性,这意味着您在 TreeViewToggle 的 Style 中所做的任何操作都无法更改它。

    这是基于Dependency Property Value Precedence(见列表)。您的 Style 触发器为 6,而显式设置为 3,因此显式设置获胜。

    您必须像这样设置边框的背景:

    <Trigger Property="IsMouseOver" Value="True">
        <Setter TargetElement="LayoutBorder" Property="Background"
            Value="{Binding Path=HighlightBrush, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
        <Setter TargetElement="LayoutBorder" Property="BorderBrush"
            Value="{Binding Path=HighlightBorderBrush, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
        <Setter TargetElement="LayoutBorder" Property="BorderThickness"
            Value="{Binding Path=HighlightBorderThickness, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
    </Trigger>
    

    编辑:以下 ControlTemplate 应该允许您取消自定义 ToggleButton:

    <ControlTemplate TargetType="{x:Type TreeViewItem}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid x:Name="header">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" MinWidth="19" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}"
                        IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" />
                <Border Name="Bd" Grid.Column="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                    <ContentPresenter x:Name="PART_Header" ContentSource="Header"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                </Border>
            </Grid>
            <ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1" Margin="19,0,0,0" />
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger SourceName="header" Property="IsMouseOver" Value="true">
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    

    【讨论】:

    • 感谢您的快速回复。我也考虑过这一点,但不确定何时何地优先重要。但是,这也不能解决问题。我将设置器更改为目标 LayoutBorder,甚至删除了 TreeViewItem 样式中 Background 属性的显式设置。通过这样做,切换按钮获得默认按钮背景,并且在鼠标悬停 oO 时背景变得透明,这很奇怪,因为如果我单步执行 TreeViewToggle 类中的 MouseEnter 事件并查看它包含的 HighightBrush #FF000000(黑色)跨度>
    • @SilverX - 我更新了绑定以使用 TemplatedParent,而不是 Self。这应该工作:-)
    • 啊,是的。我担心我不得不重新使用资源中定义的画笔。这是太棒了!谢谢! :)。希望有更好的方法来捕获只有 treeviewitem 标题的 mouseover 事件。将不再需要切换按钮和通过 TreeViewItem 将画笔信息传递给 ToggleButton。但它有效,我觉得很好:)
    • @SilverX - 我用 ControlTemplate 更新了我的答案,它应该可以让你摆脱自定义的 ToggleButton。您可能想要自定义“标题”部分,但这似乎是您想要的。
    • 哦,是的。我什至没有想过要使用触发器的 SourceName 属性。是的,这应该是正确的。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2013-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-17
    相关资源
    最近更新 更多