Blog

Styles in Silverlight - Inheritance, Precedence and Other Advanced Topics

This article is part 2 of 4 in a series attempting to cover everything I think is worth knowing about styles in Silverlight 4. The previous article in the series was Styles in Silverlight: an Introduction that covered what styles are, how they can be defined and some of their limitations.

This article focuses on some more advanced topics that are handy when building more complex applications: re-using the same styles for different types, style inheritance, style precedence and style setter precedence.

Re-Using Styles for Different Types

As discussed in the previous article, all Style elements need to specify a TargetTypethat specifies the type of class they need to be applied to. If an application uses several different types of visual elements, this can result in having to specify different styles again and again. Let's say for example our application has an Ellipse and a Rectangle and we want to have both of them have a blue fill. The straightforward way of doing this using Styles is declaring two different styles for each of them and applying those using StaticResources in XAML:

<userControl.Resources>
    <style TargetType="Ellipse" x:Key="CustomEllipseStyle">
        <setter Property="Fill" Value="Blue"/>
    </style>
    <style TargetType="Rectangle" x:Key="CustomRectangleStyle">
        <setter Property="Fill" Value="Blue"/>
    </style>
</userControl.Resources>
<stackPanel Orientation="Vertical">
    <ellipse Width="30" Height="30" Style="{StaticResource CustomEllipseStyle}"/>
    <rectangle Width="30" Height="30" Style="{StaticResource CustomRectangleStyle}" Margin="0,10,0,0"/>
</stackPanel> 

However in this particular case it's possible to combine these styles as Ellipse and Rectangle inherit from the same parent class: Shape. The Fill property that we are setting in the styles is defined in Shape, so we can define a single style with the TargetType set to Shape, then apply this Style to the elements:

<userControl.Resources>
    <style TargetType="Shape" x:Key="CustomShapeStyle">
        <setter Property="Fill" Value="Blue"/>
    </style>
</userControl.Resources>
<stackPanel Orientation="Vertical">
    <ellipse Width="30" Height="30" Style="{StaticResource CustomShapeStyle}"/>
    <rectangle Width="30" Height="30" Style="{StaticResource CustomShapeStyle}" Margin="0,10,0,0"/>
</stackPanel>

So the TargetType of the style does not necessarily have to be the type of the visual element itself. It can be a parent class of the visual element if the setters within this style only refer to properties of this parent class. The XAML code for the second example is shorter, at the same time both examples look the same:

Download the source for these two examples: Using The Same Style For Separate Classes.zip.

Style Inheritance: The BasedOn property

The Style object defines the BasedOn property which makes it easy to inherit styles. This property works just as expected: a style that defines a BasedOn style inherits all of the setters from this parent style. Only single inheritance is supported, and the length of the inheritance chain is not limited (so properties aren't just inherited from the parent, but the grandparent, great-grandparent and so on if they're set)

Let's see a simple example on how to use this property:

<userControl.Resources>
    <style x:Key="BlueBorderStyle" TargetType="Rectangle">
        <setter Property="Stroke" Value="Blue"/>
        <setter Property="StrokeThickness" Value="3"/>
    </style>
    <style x:Key="GreenFillStyle" TargetType="Rectangle">
        <setter Property="Fill" Value="Green"/>
    </style>
    <style x:Key="GreenFillStyleBasedOnBlueBorderStyle" TargetType="Rectangle" BasedOn="{StaticResource BlueBorderStyle}">
        <setter Property="Fill" Value="Green"/>
    </style>
    <style x:Key="SemiTransparentStyleBasedOnGreenFillStyleBasedOnBlueBorderStyle" TargetType="Rectangle" BasedOn="{StaticResource GreenFillStyleBasedOnBlueBorderStyle}">
        <setter Property="Opacity" Value="0.5"/>
    </style>
</userControl.Resources>
<stackPanel Orientation="Vertical">
    <rectangle Width="100" Height="50" Style="{StaticResource BlueBorderStyle}" x:Name="Rectangle1"/>
    <rectangle Width="100" Height="50" Style="{StaticResource GreenFillStyle}" Margin="0,5,0,0" x:Name="Rectangle2"/>
    <rectangle Width="100" Height="50" Style="{StaticResource GreenFillStyleBasedOnBlueBorderStyle}" Margin="0,5,0,0" x:Name="Rectangle3"/>
    <rectangle Width="100" Height="50" Style="{StaticResource SemiTransparentStyleBasedOnGreenFillStyleBasedOnBlueBorderStyle}" Margin="0,5,0,0" x:Name="Rectangle4"/>
</stackPanel> 

In the example the first rectangle uses a style with a blue border (BlueBorderStyle). The second one uses a style with a green fill (GreenFillStyle). The third one uses a Style that defines a green fill and inherits BlueBorderStyle. The fourth one uses a Style that defines 0.5 opacity and inherits the previous style. Download the source of this example here: Styles: the BasedOn Property.zip

Implicit Styling

Up to Silverlight 4 when an application had multiple instances of a visual element that needed to be styled the same way, styles had to be assigned to the individually. This isn't a big deal, however it would have been nice having a way to define the default styles for particular elements.

Let's look at a simple example of an application consisting of 4 buttons. By default all buttons should have text rendered in red on them. At the same time we want the right button to display blue text. The straightforward way to do this is the following:

<userControl.Resources>
    <style x:Key="DefaultButtonStyle" TargetType="Button">
        <setter Property="Foreground" Value="Red"/>
        <setter Property="Width" Value="150"/>
        <setter Property="Height" Value="30"/>
        <setter Property="Margin" Value="5,0,0,0"/>
    </style>
    <style x:Key="CustomButtonStyle" TargetType="Button">
        <setter Property="Foreground" Value="Blue"/>
        <setter Property="Width" Value="150"/>
        <setter Property="Height" Value="30"/>
        <setter Property="Margin" Value="5,0,0,0"/>
    </style>
</userControl.Resources>
<stackPanel Orientation="Horizontal">
    <button Content="Button 1" Style="{StaticResource DefaultButtonStyle}"/>
    <button Content="Button 2" Style="{StaticResource DefaultButtonStyle}"/>
    <button Content="Button 3" Style="{StaticResource DefaultButtonStyle}"/>
    <button Content="Custom Button" Style="{StaticResource CustomButtonStyle}"/>
</stackPanel> 

However, defining this kind of "default style" has been made possible since Silverlight 4 introduced implicit styles. Implicit styles need to be declared in the Resources part of the XAML the same way as regular styles do, except the x:Key attribute should not be declared on them. So the code for the same example using implicit styles is the following:

<userControl.Resources>
    <!-- If no x:Key property is declared, the style will be applied as implicit-->
    <style TargetType="Button">
        <setter Property="Foreground" Value="Red"/>
        <setter Property="Width" Value="150"/>
        <setter Property="Height" Value="30"/>
        <setter Property="Margin" Value="5,0,0,0"/>
    </style>
    <!-- Since the x:Key property is defined on this style, it will not be treated as implicit-->
    <style x:Key="CustomButtonStyle" TargetType="Button">
        <setter Property="Foreground" Value="Blue"/>
        <setter Property="Width" Value="150"/>
        <setter Property="Height" Value="30"/>
        <setter Property="Margin" Value="5,0,0,0"/>
    </style>
</userControl.Resources>
<stackPanel Orientation="Horizontal">
    <button Content="Button 1" />
    <button Content="Button 2" />
    <button Content="Button 3" />
    <button Content="Custom Button" Style="{StaticResource CustomButtonStyle}"/>
</stackPanel> 

Both examples look the same:

In the above example implicit styles didn't seem that much of a gain. However implicit styles help a lot by removing the need to bind default styles to all elements in an application - the larger the application, the more sense it makes to use this way of declaring default styles.

Download the source code for this example here: Implicit Styling.zip

Note that implicit styles may not always work when using templates with visual elements that are not sublclasses of Control (e.g. using a TextBlock in a DataTemplate will ignore the implicit style). For a little more detail on this see Mohamed Mosallem's blog article on the subject.

Style Precedence

Only dependency properties can be styled (more on this in the next part of the series), therefore Styles have their place in the dependency value precedence

This value precedence is as follows (through the example using the Foreground property of a Button):

  • Animations have the highest precedence. That is if the Foreground property of the Button is animated (e.g. changing the foreground color from blue to yellow using a ColorAnimation), the value that the animation sets will be applied.
  • Locally set valuesare next in the list. If no animation is applied to e.g. the Foreground of a Button and the value is set by locally assigning, then this value will be applied. Locally assigning happens the following way: 
    <button Foreground="Blue"/>
    
  • Templated properties: If none above are set, then values set in the control templates are applied. This series doesn't focus on templating, for more information on this see Scott Guthrie's post on templating.
  • Style setters: if none of the above are set, only then do the setters defined in the style get applied (if no style is defined then the framework will look for a suitable implicit style and use that if defined) 
    <userControl.Resources>
       <style x:Key="CustomButtonStyle" TargetType="Button">
          <setter Property="Foreground" Value="Blue"/>
       </style>
    </userControl.Resources>
    <button Style="{StaticResource CustomButtonStyle}" Content="Push me!"/>
    
  • Default value: dependency properties can have default values defined. If none of the above are specified, then this default value gets applied.

The most important thing in this list is when working with styles is that a style setter is only applied to the element if:

  • No animation is animating the given property
  • The given property is not set locally
  • The given property is not hard coded to a specific value in the control template (if the element has a control template)

Style Setter Precedence

When listing the Setters within the style, it is allowed to declare multiple setters for the same property. In this case the last Setter will be applied to the element.

See the following example that demonstrates this behaviour:

<userControl.Resources>
    <style x:Key="CustomButtonStyle" TargetType="Button">
        <setter Property="Foreground" Value="Red"/>
        <setter Property="Foreground" Value="Yellow"/>
        <setter Property="Foreground" Value="Green"/>
    </style>
</userControl.Resources>
<grid>
    <button Width="150" Height="30" Content="Button 1" Style="{StaticResource CustomButtonStyle}"/>
</grid> 

In the example multiple setters are declared for the Foreground property of a Button. Of these the last one - Green - gets applied indeed. Download the source for this example here: Style Setter Precedence.zip

Conclusion

In this part of the series I've covered some cases of more advanced usage of styles. These cases covered:

  • Re-Using the styles for different types: the TargetType of the Style can be a parent type of the target object as long as it only sets properties of the parent type
  • Inheriting Styles can be done using the BasedOn property. Styles support single inheritance and the depth of inheritance is not limited.
  • Implicit styling: in Silverlight 4 the default styles of elements on a page / control can be specified in a simple way using implicit styles
  • Style precedence: a Style's Setters only get applied if the object's particular property is not animated, not set locally and not set in the control template belonging to the object
  • Style Setters' precedence: setters specifying the value of the same property may be declared within the same Style. The last one of them has the highest precedence and will be used.

I hope you found this part interesting. In the next part of the series I'll be covering further advanced topics such as declaring and using styles in code behind, data binding to styles and getting style change notifications. Stay tuned!

Interested in an easy to style, performant Silverlight / WPF charting library? Give the free version of Visiblox a try!
 

Comments

[...] Re-Using Styles for Different Types: the TargetType of the Style can be a parent type of the target object as long as it only sets properties of the parent type. So for example the Fill property of both an Ellipse and Rectangle can be styled by using a Style that’s TargetType is set to Shape (as both Ellipse and Rectangle inherit from Shape) [...]

 

Nice blog Gergely :)

Minor thing: You seem to have missed the bold tag on the explanation of Local in the Style Precedence section.

 
Posted by Drew Forster

Thanks for the serie Gergely, it is good to have this type of clear and direct material to learn about silverlight 4.

 
Posted by Alfredo De Regil

Drew: thanks for the comment, I've corrected it.

Alfredo: thank you, glad you find the posts useful!

 
Posted by Gergely Orosz

Part 3&4??? Please :)

 
Posted by chadbr

[...] Styles in Silverlight – Inheritance, Precedence and Other Advanced Topics [...]

 
 
Posted by Gergely Orosz

Nice article. Good for one who is new to Silverlight.

 
Posted by Sachin Nayak

Post a comment