WPF & XAML: Multiple Style Inheritance and Markup Extensions

January 3, 2009

WPF provides a way for a Style to inherit from  another Style, using the BasedOn attribute.   This is basically single inheritance for styles.

We recently came across a “need” to inherit from multiple styles.  Our UX designers had globally restyled the look of some of our controls, and our developers had used styles and triggers for enabling & disabling (amoung other things) some local controls.

We could have created a brand new Style — basing it on the UX Style (for example) and then cutting and pasting the style elements from the second style.  However, we weren’t too keen on duplicating the second Style’s trigger logic. 

Since WPF/XAML doesn’t include a built-in way to inherit from multiple styles (as it does for inheriting from a single style), we decided to create our own way of doing just that, using a custom Markup Extension.

The MergedStylesExtension merges two styles together, and returns a new Style.  It does this by utilizing the BasedOn attribute to inherit from the first Style, and then it programmatically copies all of the setter and trigger elements from the second Style.

A basic version of it is as follows:

[MarkupExtensionReturnType(typeof(Style))]
public class MergedStylesExtension : MarkupExtension
{
   public Style BasedOn    { get; set; }
   public Style MergeStyle { get; set; }
 
   public override object ProvideValue(IServiceProvider
                                       serviceProvider)
   {
      if (null == MergeStyle)
         return BasedOn;
 
      Style newStyle = new Style(BasedOn.TargetType,
                                 BasedOn);
 
      MergeWithStyle(newStyle, MergeStyle);
 
      return newStyle;
   }   

   private static void MergeWithStyle(Style style,
                                      Style mergeStyle)
   {
      // Recursively merge with any Styles this Style
      // might be BasedOn.
      if (mergeStyle.BasedOn != null)
      {
          MergeWithStyle(style, mergeStyle.BasedOn);
      }

      // Merge the Setters...
      foreach (var setter in mergeStyle.Setters)
         style.Setters.Add(setter);
    
      // Merge the Triggers...
      foreach (var trigger in mergeStyle.Triggers)
         style.Triggers.Add(trigger);
    }
}

 

Using the custom extension method in XAML should be similar to using other, built-in extensions:

  

<Button
   Style="{ext:MergedStyles
            BasedOn={StaticResource FirstStyle}
            MergeStyle={StaticResource SecondStyle}}"
   Content="This is an example of a merged style" />

 

However, due to a WPF bug (see: http://www.hardcodet.net/2008/04/nested-markup-extension-bug), this won’t quite work.  Instead, we need to resort to property element syntax:

 
<Button 

   Content="This is an example of a merged style">
   <Button.Style>
      <ext:MergedStyles
          BasedOn="{StaticResource FirstStyle}"
          MergeStyle="{StaticResource SecondStyle}"/>
   </Button.Style>
</Button>

 

You can also define the merged style in the Resources section of your XAML file, if you intend to use it in more than one place:

<Style x:Key="MergedStyle"
       TargetType="{x:Type Control}">
    <Style.BasedOn>
       <ext:MergedStyles
           BasedOn="{StaticResource FirstStyle}"
           MergeStyle="{StaticResource SecondStyle}"/>
    </Style.BasedOn>
</Style>

For more information on creating your own custom Markup Extensions, see: http://dotnet.dzone.com/news/extend-wpf-add-your-own-keywor.


Follow

Get every new post delivered to your Inbox.