How to apply multiple styles in WPF How to apply multiple styles in WPF wpf wpf

How to apply multiple styles in WPF


I think the simple answer is that you can't do (at least in this version of WPF) what you are trying to do.

That is, for any particular element only one Style can be applied.

However, as others have stated above, maybe you can use BasedOn to help you out. Check out the following piece of loose xaml. In it you will see that I have a base style that is setting a property that exists on the base class of the element that I want to apply two styles to. And, in the second style which is based on the base style, I set another property.

So, the idea here ... is if you can somehow separate the properties that you want to set ... according the inheritance hierarchy of the element you want to set multiple styles on ... you might have a workaround.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">    <Page.Resources>        <Style x:Key="baseStyle" TargetType="FrameworkElement">            <Setter Property="HorizontalAlignment" Value="Left"/>        </Style>        <Style TargetType="Button" BasedOn="{StaticResource baseStyle}">            <Setter Property="Content" Value="Hello World"/>        </Style>    </Page.Resources>    <Grid>        <Button Width="200" Height="50"/>    </Grid></Page>


Hope this helps.

Note:

One thing in particular to note. If you change the TargetType in the second style (in first set of xaml above) to ButtonBase, the two Styles do not get applied. However, check out the following xaml below to get around that restriction. Basically, it means you need to give the Style a key and reference it with that key.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">    <Page.Resources>        <Style x:Key="baseStyle" TargetType="FrameworkElement">            <Setter Property="HorizontalAlignment" Value="Left"/>        </Style>        <Style x:Key="derivedStyle" TargetType="ButtonBase" BasedOn="{StaticResource baseStyle}">            <Setter Property="Content" Value="Hello World"/>        </Style>    </Page.Resources>    <Grid>        <Button Width="200" Height="50" Style="{StaticResource derivedStyle}"/>    </Grid></Page>


Bea Stollnitz had a good blog post about using a markup extension for this, under the heading "How can I set multiple styles in WPF?"

That blog is dead now, so I'm reproducing the post here:

WPF and Silverlight both offer the ability to derive a Style fromanother Style through the “BasedOn” property. This feature enablesdevelopers to organize their styles using a hierarchy similar to classinheritance. Consider the following styles:

<Style TargetType="Button" x:Key="BaseButtonStyle">    <Setter Property="Margin" Value="10" /></Style><Style TargetType="Button" x:Key="RedButtonStyle" BasedOn="{StaticResource BaseButtonStyle}">    <Setter Property="Foreground" Value="Red" /></Style>

With this syntax, a Button that uses RedButtonStyle will have itsForeground property set to Red and its Margin property set to 10.

This feature has been around in WPF for a long time, and it’s new inSilverlight 3.

What if you want to set more than one style on an element? Neither WPFnor Silverlight provide a solution for this problem out of the box.Fortunately there are ways to implement this behavior in WPF, which Iwill discuss in this blog post.

WPF and Silverlight use markup extensions to provide properties withvalues that require some logic to obtain. Markup extensions are easilyrecognizable by the presence of curly brackets surrounding them inXAML. For example, the {Binding} markup extension contains logic tofetch a value from a data source and update it when changes occur; the{StaticResource} markup extension contains logic to grab a value froma resource dictionary based on a key. Fortunately for us, WPF allowsusers to write their own custom markup extensions. This feature is notyet present in Silverlight, so the solution in this blog is onlyapplicable to WPF.

Othershave written great solutions to merge two styles using markupextensions. However, I wanted a solution that provided the ability tomerge an unlimited number of styles, which is a little bit trickier.

Writing a markup extension is straightforward. The first step is tocreate a class that derives from MarkupExtension, and use theMarkupExtensionReturnType attribute to indicate that you intend thevalue returned from your markup extension to be of type Style.

[MarkupExtensionReturnType(typeof(Style))]public class MultiStyleExtension : MarkupExtension{}

Specifying inputs to the markup extension

We’d like to give users of our markup extension a simple way tospecify the styles to be merged. There are essentially two ways inwhich the user can specify inputs to a markup extension. The user canset properties or pass parameters to the constructor. Since in thisscenario the user needs the ability to specify an unlimited number ofstyles, my first approach was to create a constructor that takes anynumber of strings using the “params” keyword:

public MultiStyleExtension(params string[] inputResourceKeys){}

My goal was to be able to write the inputs as follows:

<Button Style="{local:MultiStyle BigButtonStyle, GreenButtonStyle}" … />

Notice the comma separating the different style keys. Unfortunately,custom markup extensions don’t support an unlimited number ofconstructor parameters, so this approach results in a compile error.If I knew in advance how many styles I wanted to merge, I could haveused the same XAML syntax with a constructor taking the desired numberof strings:

public MultiStyleExtension(string inputResourceKey1, string inputResourceKey2){}

As a workaround, I decided to have the constructor parameter take asingle string that specifies the style names separated by spaces. Thesyntax isn’t too bad:

<Button Style="{local:MultiStyle BigButtonStyle GreenButtonStyle}"… />

private string[] resourceKeys;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.");    }}

Calculating the output of the markup extension

To calculate the output of a markup extension, we need to override amethod from MarkupExtension called “ProvideValue”. The value returnedfrom this method will be set in the target of the markup extension.

I started by creating an extension method for Style that knows how tomerge two styles. The code for this method is quite simple:

public static void Merge(this Style style1, Style style2){    if (style1 == null)    {        throw new ArgumentNullException("style1");    }    if (style2 == null)    {        throw new ArgumentNullException("style2");    }    if (style1.TargetType.IsAssignableFrom(style2.TargetType))    {        style1.TargetType = style2.TargetType;    }    if (style2.BasedOn != null)    {        Merge(style1, style2.BasedOn);    }    foreach (SetterBase currentSetter in style2.Setters)    {        style1.Setters.Add(currentSetter);    }    foreach (TriggerBase currentTrigger in style2.Triggers)    {        style1.Triggers.Add(currentTrigger);    }    // This code is only needed when using DynamicResources.    foreach (object key in style2.Resources.Keys)    {        style1.Resources[key] = style2.Resources[key];    }}

With the logic above, the first style is modified to include allinformation from the second. If there are conflicts (e.g. both styleshave a setter for the same property), the second style wins. Noticethat aside from copying styles and triggers, I also took into accountthe TargetType and BasedOn values as well as any resources the secondstyle may have. For the TargetType of the merged style, I usedwhichever type is more derived. If the second style has a BasedOnstyle, I merge its hierarchy of styles recursively. If it hasresources, I copy them over to the first style. If those resources arereferred to using {StaticResource}, they’re statically resolved beforethis merge code executes, and therefore it isn’t necessary to movethem. I added this code in case we’re using DynamicResources.

The extension method shown above enables the following syntax:

style1.Merge(style2);

This syntax is useful provided that I have instances of both styleswithin ProvideValue. Well, I don’t. All I get from the constructor isa list of string keys for those styles. If there was support forparams in the constructor parameters, I could have used the followingsyntax to get the actual style instances:

<Button Style="{local:MultiStyle {StaticResource BigButtonStyle}, {StaticResource GreenButtonStyle}}" … />
public MultiStyleExtension(params Style[] styles){}

But that doesn’t work. And even if the params limitation didn’t exist,we would probably hit another limitation of markup extensions, wherewe would have to use property-element syntax instead of attributesyntax to specify the static resources, which is verbose andcumbersome (I explain this bug better in a previous blogpost).And even if both those limitations didn’t exist, I would still ratherwrite the list of styles using just their names – it is shorter andsimpler to read than a StaticResource for each one.

The solution is to create a StaticResourceExtension using code. Givena style key of type string and a service provider, I can useStaticResourceExtension to retrieve the actual style instance. Here isthe syntax:

Style currentStyle = new StaticResourceExtension(currentResourceKey).ProvideValue(serviceProvider)

as Style;

Now we have all the pieces needed to write the ProvideValue method:

public override object ProvideValue(IServiceProvider serviceProvider){    Style resultStyle = new Style();    foreach (string currentResourceKey in resourceKeys)    {        Style currentStyle = new StaticResourceExtension(currentResourceKey).ProvideValue(serviceProvider)

as Style;

        if (currentStyle == null)        {            throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + ".");        }        resultStyle.Merge(currentStyle);    }    return resultStyle;}

Here is a complete example of the usage of the MultiStyle markupextension:

<Window.Resources>    <Style TargetType="Button" x:Key="SmallButtonStyle">        <Setter Property="Width" Value="120" />        <Setter Property="Height" Value="25" />        <Setter Property="FontSize" Value="12" />    </Style>    <Style TargetType="Button" x:Key="GreenButtonStyle">        <Setter Property="Foreground" Value="Green" />    </Style>    <Style TargetType="Button" x:Key="BoldButtonStyle">        <Setter Property="FontWeight" Value="Bold" />    </Style></Window.Resources><Button Style="{local:MultiStyle SmallButtonStyle GreenButtonStyle BoldButtonStyle}" Content="Small, green, bold" />

enter image description here


But you can extend from another.. take a look at the BasedOn property

<Style TargetType="TextBlock">      <Setter Property="Margin" Value="3" /></Style><Style x:Key="AlwaysVerticalStyle" TargetType="TextBlock"        BasedOn="{StaticResource {x:Type TextBlock}}">     <Setter Property="VerticalAlignment" Value="Top" /></Style>