Search code examples
c#wpfwpf-styleitemcontainerstyle

ItemContainerStyle cleared by loading custom style


So, I have this issue with making a custom treeview with custom treeviewitem:s where the ItemContainerStyle gets cleared by loading the style from the custom style.

It work like this. I have custom MyTreeViewItem based on TreeViewItem.

  <TreeViewItem x:Class="UI.MyTreeViewItem"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
    
        <TreeViewItem.Resources>
    
            <Style x:Key="MyTreeViewItemStyle" TargetType="TreeViewItem">
                <Setter Property="Background" Value="#AEFFC1" />
            </Style>
    
        </TreeViewItem.Resources>
    
    </TreeViewItem>

As you can see I have just have a simple coloring here just to make sure that the styling it self works. This how ever wont load unless I do like this in code behind.

EDIT: I know things like coloring don't need to be put here as there was intended to be a template here instead. How ever since noting really worked, I just stripped this down to the bones to make sure I put something super simple in that I know should work in case it was becaouse of the template it self.

    public partial class MyTreeViewItem : TreeViewItem
    {
        public MyTreeViewItem()
        {
            InitializeComponent();
            this.Loaded += MyTreeViewItem_Loaded;          
        }

        private void MyTreeViewItem_Loaded(object sender, RoutedEventArgs e)
        {
           this.Style = Resources["MyTreeViewItemStyle"] as Style;
        }
   }

This works grate. Have used this several other times with other controls to have custom styling for controls needed to be loaded up with out having to bother to "restyle" everything over and over again.

How ever there are a issue I come across with this. And that is when ItemContainerStyle is being used of this kind of custom styled controller.

<local:BaseTreeView x:Class="My.Navigator.NavigatorTreeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:ui="clr-namespace:UI;assembly=BaseCode" 
             d:DesignHeight="450" d:DesignWidth="800">

<ui:MyTreeView ItemsSource="{Binding Path=Nodes}">   
...

        <ui:MyTreeView.ItemContainerStyle>
            <Style TargetType="{x:Type ui:MyTreeViewItem}">
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />

                <EventSetter Event="Selected" Handler="TreeView_SelectedItemChanged" />
                <EventSetter Event="Expanded" Handler="TreeView_NodeExpanded" />
                <EventSetter Event="Collapsed" Handler="TreeView_NodeCollapsed" />

                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="FontWeight" Value="Bold" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ui:MyTreeView.ItemContainerStyle>
    </ui:MyTreeView>
</local:BaseTreeView>

This ui:MyTreeView.ItemContainerStyle as you see above gets totally ignored once the style is loaded, by this.Style = Resources["MyTreeViewItemStyle"] as Style; in the MyTreeViewItem_Loaded. That means these Setters, EventSetters and Triggers will not fire at all, as they still are needed to be able to be added as as additional rules.

How can this be solved, so that the predefined styling in the custom control can be loaded and by using this control, you can still can hook up unique rules, like above with out having the the predefined overrule them?


Solution

  • It's not really clear why you are doing what you are doing. All I can say is that you are overwriting the Style value by assigning it explicitly after the control was initialized with the value from the TreeView.ItemContainerStyle.

    Normally, on a UserControl, you would set the properties locally on the element:

    <TreeViewItem x:Class="UI.MyTreeViewItem" 
                  ...
                  d:DesignHeight="450" d:DesignWidth="800"
                  Background="#AEFFC1">
    </TreeViewItem>
    

    or in code-behind:

    private void MyTreeViewItem_Loaded(object sender, RoutedEventArgs e)  
    {
      this.Background = 
        new SolidColorBrush(ColorConverter.ConvertFromString("#AEFFC1"));
     }
    

    When writing a custom Control, you would provide a default Style in Generic.xaml. This is the best solution as it allows the control to be styled (where custom styles are allowed to override default values provided by the default Style). External styles are merged implicitly. You should prefer a custom Control over a UserControl.

    Your current code does not allow styling as you forcefully override values provided by a custom Style:

    // Overwrite previous property value.
    this.Style = someValue;
    

    This is programming 101, first grade: an assignment always overwrites the old value (reference) of the variable.

    Assumming that you are knowing what you are doing and you don't want to use one of the above solution, you must manually merge both styles using the Style.BasedOn property:

    private void MyTreeViewItem_Loaded(object sender, RoutedEventArgs e)
    {
      var defaultStyle = Resources["MyTreeViewItemStyle"] as Style;
      defaultStyle.BasedOn = this.ItemContainerStyle;
      this.Style = defaultStyle;
    }
    

    See: Control authoring overview: Models for Control Authoring