Search code examples
xamlwpf-controls

WPF, custom controls, and style/theme inheritance


I'm working on an already existing project where I can't make the styling work the way I want.

I start from an application where:

  • there is a WhistlerBlue.xaml file in a Resources folder.
  • the styles inside the file use TargetType to automatically apply them
  • there is a style defined for the Expander control (more on this, next)
  • I don't want to mess with this file.
  • the file is explicitly used in the application via merged resources in the App.xaml

Up to this point, everything works fine.


Then...

I'm building a custom control which is derived from Expander:

internal partial class TestExpander : Expander

By creating the custom control a Theme folder has been added to the project by VStudio, with the usual Generic.xaml file inside. In this file I've created the style for my custom control:

<Style TargetType="{x:Type local:TestExpander}" BasedOn="{StaticResource {x:Type Expander}}">
    <Style.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibility"/>
    </Style.Resources>

    <Setter Property="ExpandDirection" Value="Down"/>
    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Top"/>
    <Setter Property="Cursor" Value="Hand"/>
</Style>

Obviously in the code of the custom control I have:

static TestExpander()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TestExpander), new FrameworkPropertyMetadata(typeof(TestExpander)));
        }

What I expect is for my TestExpander to inherit the Expander style defined in WhistlerBlue.xaml, add it's own customizations via its own style, and they all lived happily ever after.

It's not happening. Where am I wrong?


Solution

  • So when we're creating our architecture of inheritance for various things it's easy to forget the order of how things receive their inherited parts. So when we for example place a BasedOn that's looking directly at x:Type then it's just going to go look for it in the order of inheritance it has available right?

    By that when it's looking for x:Type and it's not aware of an override in another dictionary...he just goes straight to the default original. However if we inject that dictionary that includes what you're wanting to inherit from via ResourceDictionary.MergedDictionaries then it knows to go look at him first before he gets back to the framework defaults and all is good in the world.

    On a side note though, when you merge dictionaries for inheritance in such a way you have to be attentive to how you're doing it or you can cause a performance hit. Since by saying it's a merged dictionary....at runtime it will take everything in the dictionary being merged and make a copy of all of it over into the new one.

    So for this instance....if it were me, I would probably just omit that generic.xaml dictionary content if it's not really necessary and place that template in the existing dictionary just under where the template it's looking to inherit from is placed. Then you're not making another copy of an entire dictionary, it will still get its inheritance, and life should be peachy. Hope this helps. :)