[ACCEPTED]-XAML Combine styles going beyond BasedOn?-styles

Accepted answer
Score: 65

You can make a custom markup extensions 21 that will merge styles properties and triggers 20 into a single style. All you need to do 19 is add a MarkupExtension-derived class to your namespace 18 with the MarkupExtensionReturnType attribute defined and you're off 17 and running.

Here is an extension that will 16 allow you to merge styles using a "css-like" syntax.


public class MultiStyleExtension : MarkupExtension
    private string[] resourceKeys;

    /// <summary>
    /// Public constructor.
    /// </summary>
    /// <param name="inputResourceKeys">The constructor input should be a string consisting of one or more style names separated by spaces.</param>
    public MultiStyleExtension(string inputResourceKeys)
        if (inputResourceKeys == null)
            throw new ArgumentNullException("inputResourceKeys");
        this.resourceKeys = inputResourceKeys.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        if (this.resourceKeys.Length == 0)
            throw new ArgumentException("No input resource keys specified.");

    /// <summary>
    /// Returns a style that merges all styles with the keys specified in the constructor.
    /// </summary>
    /// <param name="serviceProvider">The service provider for this markup extension.</param>
    /// <returns>A style that merges all styles with the keys specified in the constructor.</returns>
    public override object ProvideValue(IServiceProvider serviceProvider)
        Style resultStyle = new Style();
        foreach (string currentResourceKey in resourceKeys)
            object key = currentResourceKey;
            if (currentResourceKey == ".")
                IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
                key = service.TargetObject.GetType();
            Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style;
            if (currentStyle == null)
                throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + ".");
        return resultStyle;

public static class MultiStyleMethods
    /// <summary>
    /// Merges the two styles passed as parameters. The first style will be modified to include any 
    /// information present in the second. If there are collisions, the second style takes priority.
    /// </summary>
    /// <param name="style1">First style to merge, which will be modified to include information from the second one.</param>
    /// <param name="style2">Second style to merge.</param>
    public static void Merge(this Style style1, Style style2)
        if(style1 == null)
            throw new ArgumentNullException("style1");
        if(style2 == null)
            throw new ArgumentNullException("style2");
            style1.TargetType = style2.TargetType;
        if(style2.BasedOn != null)
            Merge(style1, style2.BasedOn);
        foreach(SetterBase currentSetter in style2.Setters)
        foreach(TriggerBase currentTrigger in style2.Triggers)
        // This code is only needed when using DynamicResources.
        foreach(object key in style2.Resources.Keys)
            style1.Resources[key] = style2.Resources[key];

Your example would then be solved by going:

<Style x:key="Combined" BasedOn="{local:MultiStyle A B}">
      ... other properties.

We 15 have defined a new style named "Combined" by 14 merging two other styles "A" and 13 "B" within the built-in BasedOn attribute 12 (used for style inheritance). We can optionally 11 add other properties to the new "Combined" style 10 as per usual.

Other Examples:

Here, we define 4 button styles, and 9 can use them in various combinations with 8 little repetition:

    <Style TargetType="Button" x:Key="ButtonStyle">
        <Setter Property="Width" Value="120" />
        <Setter Property="Height" Value="25" />
        <Setter Property="FontSize" Value="12" />
    <Style TargetType="Button" x:Key="GreenButtonStyle">
        <Setter Property="Foreground" Value="Green" />
    <Style TargetType="Button" x:Key="RedButtonStyle">
        <Setter Property="Foreground" Value="Red" />
    <Style TargetType="Button" x:Key="BoldButtonStyle">
        <Setter Property="FontWeight" Value="Bold" />

<Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle}" Content="Green Button" />
<Button Style="{local:MultiStyle ButtonStyle RedButtonStyle}" Content="Red Button" />
<Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle BoldButtonStyle}" Content="green, bold button" />
<Button Style="{local:MultiStyle ButtonStyle RedButtonStyle BoldButtonStyle}" Content="red, bold button" />

You can even use the "." syntax 7 to merge the "current" default 6 style for a type (context-dependent) with 5 some additional styles:

<Button Style="{local:MultiStyle . GreenButtonStyle BoldButtonStyle}"/>

The above will merge 4 the default style for TargetType="{x:Type Button}" with the two supplemental 3 styles.


I found the original idea for the 2 MultiStyleExtension at bea.stollnitz.com and modified it to support the "." notation 1 to reference the current style.

Score: 4

You can use BasedOn property in style, for example:

<Style x:Key="BaseButtons" TargetType="{x:Type Button}">
        <Setter Property="BorderThickness" Value="0"></Setter>
        <Setter Property="Background" Value="Transparent"></Setter>
        <Setter Property="Cursor" Value="Hand"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
<Style x:Key="ManageButtons" TargetType="{x:Type Button}" BasedOn="{StaticResource BaseButtons}">
        <Setter Property="Height" Value="50"></Setter>
        <Setter Property="Width" Value="50"></Setter>
<Style x:Key="ManageStartButton" TargetType="{x:Type Button}" BasedOn="{StaticResource BaseButtons}">
        <Setter Property="FontSize" Value="16"></Setter>

and 1 use:

<Button Style="{StaticResource ManageButtons}"></Button>
<Button Style="{StaticResource ManageStartButton}"></Button>

More Related questions