Wednesday, February 9, 2011

How to apply multiple styles in WPF

In WPF, how would I apply multiple styles to a FrameworkElement? For instance, I have a control which already has a style. I also have a separate style which I would like to add to it without blowing away the first one. The styles have different TargetTypes, so I can't just extend one with the other.

  • 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>
    
    From Arcturus
  • you can define a style like this:

        <Style x:Key=”buttonStyle”>
        <Setter Property=”Button.FontSize” Value=”22”/>
        <Setter Property=”Button.Background” Value=”Purple”/>
        <Setter Property=”Button.Foreground” Value=”White”/>
        <Setter Property=”Button.Height” Value=”50”/>
        <Setter Property=”Button.Width” Value=”50”/>
        <Setter Property=”Button.RenderTransformOrigin” Value=”.5,.5”/>
        <Setter Property=”Button.RenderTransform”>
        <Setter.Value>
        <RotateTransform Angle=”10”/>
        </Setter.Value>
        </Setter>
    </Style>
    

    and apply like this:

    <Button Style=”{StaticResource buttonStyle}”>1</Button>
    <Button Style=”{StaticResource buttonStyle}”>2</Button>
    <Button Style=”{StaticResource buttonStyle}”>3</Button>
    

    if you wants to define another style just give it another name like:

    <Style x:Key=”buttonStyleBlue”>
    <Setter Property=”Button.FontSize” Value=”22”/>
    <Setter Property=”Button.Background” Value=”Blue”/>
    <Setter Property=”Button.Foreground” Value=”Black”/>
    <Setter Property=”Button.Height” Value=”50”/>
    <Setter Property=”Button.Width” Value=”50”/>
    <Setter Property=”Button.RenderTransformOrigin” Value=”.5,.5”/>
    <Setter Property=”Button.RenderTransform”>
    <Setter.Value>
    <RotateTransform Angle=”10”/>
    </Setter.Value>
    </Setter>
    

    and then apply it with the x:Key you defined it

    Steav : the author asked for appling multiple styles, tho
    From darkdog
  • If I could use BasedOn, that would definitely work. But, because the two styles have to have different TargetTypes, I can't use BasedOn.

    From MojoFilter
  • if you are not touching any specific properties, you can get all base and common properties to the style which's target type would be FrameworkElement. then, you can create specific flavours for each target types you need, without need of copying all those common properties again.

    From Greg
  • 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.
    cplotts

    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>
    
    From cplotts
  • You can probably get something similar if applying this to a collection of items by the use of a StyleSelector, i have used this to approach a similar problem in using different styles on TreeViewItems depending on the bound object type in the tree. You may have to modify the class below slightly to adjust to your particular approach but hopefully this will get you started

    public class MyTreeStyleSelector : StyleSelector
    {
        public Style DefaultStyle
        {
            get;
            set;
        }
    
        public Style NewStyle
        {
            get;
            set;
        }
    
        public override Style SelectStyle(object item, DependencyObject container)
        {
            ItemsControl ctrl = ItemsControl.ItemsControlFromItemContainer(container);
    
            //apply to only the first element in the container (new node)
            if (item == ctrl.Items[0])
            {
                return NewStyle;
            }
            else
            {
                //otherwise use the default style
                return DefaultStyle;
            }
        }
    }
    

    You then apply this as so

     <TreeView>
         <TreeView.ItemContainerStyleSelector
             <myassembly:MyTreeStyleSelector DefaultStyle="{StaticResource DefaultItemStyle}"
                                             NewStyle="{StaticResource NewItemStyle}" />
         </TreeView.ItemContainerStyleSelector>
      </TreeView>
    
    From Dave
  • WPF/XAML doesn't provide this functionality natively, but it does provide the extensibility to allow you to do what you want.

    We ran into the same need, and ended up creating our own XAML Markup Extension (which we called "MergedStylesExtension") to allow us to create a new Style from two other styles (which, if needed, could probably be used multiple times in a row to inherit from even more styles).

    Due to a WPF/XAML bug, we need to use property element syntax to use it, but other than that it seems to work ok. E.g.,

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

    I recently wrote about it here: http://swdeveloper.wordpress.com/2009/01/03/wpf-xaml-multiple-style-inheritance-and-markup-extensions/

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

    From Wilka

0 comments:

Post a Comment