Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Settings triggers on 'self' properties passed down from parent templates (WPF)

I'm currently using a rather complex method of templating/styling the WPF TreeView control. This consists of 3 styles, one for the TreeView, one for the TreeViewItem, and one for a ToggleButton. I had everything working great until I thought it would be a cool idea to expose some customizing depedency properties on the TreeView. A couple of them include ItemBackgroundBrush, ItemHighlightBrush, and ItemSelectedBrush. So you can define or change each of these values while working with the TreeView markup instead of having to continuously change the templates (which are stored in a resource dictionary). I have all of my brushes affecting the ToggleButton, aptly named TreeViewToggle and passed via binding down the visual tree. The reason for this is that IsMouseOver of the TreeViewitem is true even when the mouse pointer is over one of the item's children, this I suppose is because it's ItemsPanel is still technically part of the item. To surpass this I handle the IsMouseOver trigger of the TreeViewToggle so hover/selection brushes are applied to the appropriate headers. I guess I'll post the code below to give you an idea of what I'm doing but I warn you it's getting pretty darn large lol. Everything works fine up until the IsMouseOver trigger of the TreeViewToggleStyle.. I've stepped over the Highlight brush property and it contains the brush passed down from the TreeViewEx ItemHighlightBackground property but I cannot for the life of me get it to set to the border (LayoutRoot)'s background property... (the current setter code is only one of many attempts at different binding sytaxes.. I have tried so many examples of self binding I've seen on the web and none of them seem to work :( Again, sorry for the huge post but I wasn't sure what information would be relevant and what wouldn't. Thanks in advance, Ryan.

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

I also have 2 clases defined with their dependency properties exposed:

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));
}
like image 317
SilverX Avatar asked Apr 08 '11 21:04

SilverX


1 Answers

you set the Background property of your TreeViewToggle in the ControlTemplate of your SolaceTreeViewItemStyle, so that means nothing you do in the TreeViewToggle's Style can change it.

This is based on the Dependency Property Value Precedence (see the list). Your Style trigger is at a 6, while the explicit setting is at a 3, so the explicit setting wins.

You would have to set the Background of the Border like this instead:

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

EDIT: The following ControlTemplate should allow you to do away with a custom 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>
like image 178
CodeNaked Avatar answered Sep 29 '22 06:09

CodeNaked