[This topic is pre-release documentation and is subject to change in future releases of Microsoft Silverlight.]

Changing the Appearance of an Existing Control in Silverlight

Introduction

This walkthrough shows you how to create a style that can be applied to multiple controls and how to create a ControlTemplate to change the skin of a control.

Run View
Language:

The following tools are required to complete this walkthrough and are available from the Silverlight download site.

  • Silverlight version 2 Beta 1.

  • Visual Studio 2008. 

  • Silverlight Tools Beta 1 for Visual Studio 2008.

Creating a Style for a Button

To create an application that contains buttons

  1. Create a Silverlight project in Visual Studio. (For instructions, see Creating an Application for Silverlight.) The project creates a default UserControl that has a Grid as its root element.

    cs

    <Grid x:Name="LayoutRoot" Background="White">
    
    </Grid>
    
    
  2. Replace the Grid with a StackPanel that contains three buttons.

    cs

    <StackPanel>
    
        <!--Add the Style as a StackPanel.Resource here.-->
    
        <Button Content="Button 1"/>
        <Button Content="Button 2"/>
        <Button Content="Button 3"/>
    </StackPanel>
    
    

This section of the walkthrough shows you how to give these buttons a uniform appearance. Instead of setting the properties to the same values on each Button, you can set the properties in a Style and apply the Style to each Button.

To create a style for the buttons

  1. Add the following XAML to the StackPanel in place of the comment "Add the Style as a StackPanel.Resource here." This creates a style as a resource that can be used by any child element of the StackPanel. The style sets the Background, Foreground, FontSize, Width, Height, and Margin properties for any Button that uses the style.

    cs

    <StackPanel.Resources>
        <Style TargetType="Button" x:Key="buttonStyle">
            <Setter Property="Background" Value="Green"/>
            <Setter Property="Foreground" Value="Navy"/>
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="40"/>
            <Setter Property="Margin" Value="10"/>
        </Style>
    </StackPanel.Resources>
    
    
  2. Assign the style that you just created to the Style property of each button that you created in step 2 of the previous procedure. Because the Style is a resource, you must use the StaticResource Markup Extension to reference the Style.

    cs

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

When you run your application, you will see three buttons that look the same because the property values you specified in the style are applied to each button.

Creating a New ControlTemplate for Button

When you want to change the skin of a control, you need to replace its ControlTemplate. When you create a new ControlTemplate, you can redefine its visual structure and visual behavior without changing the control's logic. A ControlTemplate is defined in XAML, so you can create a new one without writing any code. A control interacts with its ControlTemplate by referencing the elements within the template. The control provides a control contract, which specifies the elements that the control expects to find in the ControlTemplate. For more information, see Styling and Templating Overview.

The control contract for the Button is specified in Button Styles and Templates. The button expects the following elements to be in its ControlTemplate:

  • RootElement

  • FocusVisualElement

  • MouseOver State

  • Pressed State

  • Disabled State

  • Normal State

This remainder of the walkthrough shows you how to create a ControlTemplate that contains these elements and specifies an appearance for the Button that is different that its default appearance.

To create a ControlTemplate for a button

  1. Replace the previously created Style with the following XAML. This example adds a Setter to the Style that sets the Template property of the button. You normally create a ControlTemplate as part of the style of the control to keep the Style and ControlTemplate together, so you use the property element syntax for the Value property. For more information about property element syntax, see XAML Overview.

    cs

    <Style TargetType="Button" x:Key="buttonStyle">
        <Setter Property="Background" Value="Green"/>
        <Setter Property="Foreground" Value="Navy"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Width" Value="100"/>
        <Setter Property="Height" Value="40"/>
        <Setter Property="Margin" Value="10"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <!--Add the Grid ELEMENT_Root here in the next step.-->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    
  2. Add a Grid as the root element of the ControlTemplate, after the comment "Add the Grid, RootElement, here in the next step." This is the first element in the control contract; all of the subsequent elements that you add are descendents (child elements or deeper) of RootElement.

    cs

    <Grid x:Name="ELEMENT_Root">
        <Grid.Resources>
            <!--Add the constant resources here.-->
    
            <!--Add the Storyboard objects that specify states here.-->
        </Grid.Resources>
    
        <!--Add child FrameworkElement objects here.
            Keep the elements in the order that is specified.-->
    
    </Grid>
    
    

The comments in the Grid are to help you to add elements to the appropriate places in subsequent steps. The grid can contain child elements and resources. The child elements of the grid inherit from FrameworkElement and specify the appearance and structure of the control. The resources of the grid are objects that are used by the elements within the grid. A resource can be referenced by a FrameworkElement or by another resource.

The two types of parts that are specified in a control contract are FrameworkElement and Storyboard. Elements that inherit from FrameworkElement are child elements of the Grid. Elements of type Storyboard are resources. To keep the ControlTemplate organized, the previous example divided the grid's resources into two sections: a section that contains "constant" resources, such as brushes, and a section that contains the Storyboard elements that are part of the control contract. The examples of control templates in Control Styles and Templates follow this structure, so by using the structure in this walkthrough, you will become familiar with where to look for elements in the template examples in the SDK.

To specify the appearance of the control

  1. Add the following elements between the <Grid.Resources> tags at the comment "Add the constant resources here."

    cs

    <!--Add the constant resources here.-->
    <LinearGradientBrush x:Key="ReflectionGradient" 
        StartPoint="0,1" EndPoint="0,0">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="#80FFFFFF" Offset="0" />
            <GradientStop Color="#50FFFFFF" Offset="0.5" />
            <GradientStop Color="#80FFFFFF" Offset="0.5" />
            <GradientStop Color="#C0FFFFFF" Offset="1" />
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    
    <ScaleTransform x:Key="scale_1_1-3" ScaleX="1" ScaleY="1.4" />
    
    <RadialGradientBrush x:Key="HighlightGradient">
        <RadialGradientBrush.GradientStops>
            <GradientStop Color="#FFFFFF" Offset="0" />
            <GradientStop Color="#AAFFFFFF" Offset="0.3" />
            <GradientStop Color="#55FFFFFF" Offset="0.6" />
            <GradientStop Color="Transparent" Offset="1" />
        </RadialGradientBrush.GradientStops>
    </RadialGradientBrush>
    
    <LinearGradientBrush x:Key="FocusedStrokeBrush" 
        StartPoint="0.5,0"
        EndPoint="0.5,1">
    
        <GradientStop Color="#B2FFFFFF" Offset="0" />
        <GradientStop Color="#51FFFFFF" Offset="1" />
        <GradientStop Color="#66FFFFFF" Offset="0.325" />
        <GradientStop Color="#1EFFFFFF" Offset="0.325" />
    </LinearGradientBrush>
    
    <SolidColorBrush x:Key="DisabledBrush" Color="#A5FFFFFF" />
    
    <SolidColorBrush x:Key="AccentBrush" Color="#FFFFFFFF" />
    
    
  2. Add the following elements after the closing </Grid.Resources> tag, at the comment "Add the elements that specify appearance here." Notice that you are adding multiple Rectangle objects as child elements to the Grid. The Grid allows elements to share the same position on the screen, and the order in which they are rendered is determined by the order in which they are added to the Grid. In this example, the Rectangle with the x:NameBase is rendered before the Rectangle with the x:NameReflection. Also notice that the Fill property of the Rectangle called Base is set to {TemplateBinding Background}. This means that that the Background property of the Button determines the Fill property of Base. This enables you to set the Background property on individual buttons, and the ControlTemplate will use the value you gave. This example also adds the FocusVisualElement to the ControlTemplate, fulfilling the second element in the control contract.

    cs

    <!--Add the elements that specify appearance here.
        Keep the elements in the order that is specified
        in the QuickStart.-->
    <!--Main color of the button-->
    <Rectangle x:Name="Base" 
        RadiusY="2" RadiusX="2" 
        Fill="{TemplateBinding Background}"  />
    
    <!--Reflection effect-->
    <Rectangle x:Name="Reflection" 
        RadiusY="2" RadiusX="2" 
        Fill="{StaticResource ReflectionGradient}" />
    
    <!--3D Effect-->
    <Rectangle x:Name="BlurBorder" 
        RadiusY="2" RadiusX="2" Stroke="#60000000" StrokeThickness="1.5" />
    <Rectangle x:Name="ThinBorder" RadiusY="2" 
        RadiusX="2" Stroke="#90000000" StrokeThickness="0.5" />
    
    <!--Rectangles used as the FocusVisual-->
    <Grid x:Name="FocusVisualElement" Visibility="Collapsed">
        <Rectangle RadiusX="3" RadiusY="3" Margin="2" 
            Stroke="{StaticResource AccentBrush}" 
            StrokeThickness="1" />
    
        <Rectangle RadiusX="3" RadiusY="3" 
            Stroke="{TemplateBinding Background}" 
            StrokeThickness="2" />
    </Grid>
    
    
  3. Add the following ContentPresenter after the elements you added in the previous step. The ContentPresenter is responsible for displaying the content of the button. Like the BaseRectangle, the properties of the ContentPresenter use the TemplateBinding Markup Extension to bind the properties on the ContentPresenter to the properties on the Button.

    cs

    <ContentPresenter
      Content="{TemplateBinding Content}"
      ContentTemplate="{TemplateBinding ContentTemplate}"
      FontFamily="{TemplateBinding FontFamily}"
      FontSize="{TemplateBinding FontSize}"
      FontStretch="{TemplateBinding FontStretch}"
      FontStyle="{TemplateBinding FontStyle}"
      FontWeight="{TemplateBinding FontWeight}"
      Foreground="{TemplateBinding Foreground}"
      HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
      Padding="{TemplateBinding Padding}"
      TextAlignment="{TemplateBinding TextAlignment}"
      TextDecorations="{TemplateBinding TextDecorations}"
      TextWrapping="{TemplateBinding TextWrapping}"
      VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
      Margin="4,5,4,4" />
    
    

Now you can run the application at this point and see that the buttons look very different than how they originally looked.

To run the application

  1. On the Debug menu, click Start Debugging.

Adding the State Elements to the ControlTemplate

If you run the application and move the mouse over or click a button, you will notice that there is no visual feedback that indicates what the user is doing. The next step is to add the elements that the control uses to visually indicate the control's state. These are the Storyboard elements in the control contract. The four elements in the button's control contract that specify the button's states are the MouseOver State, the Pressed State, the Disabled State, and the Normal State.

To add the MouseOver State to the ControlTemplate

  1. Add the following Rectangle to the ControlTemplate immediately before the ContentPresenter. This Rectangle uses a RadialGradientBrush (which is defined as a resource) to cause the button to appear highlighted. The Opacity of the Rectangle is set to 0 because in its normal state, the Rectangle should be completely transparent.

    cs

    <!--Rectangle used for the MouseOver State-->
    <Rectangle x:Name="Highlight" Opacity="0" 
        RenderTransform="{StaticResource scale_1_1-3}" 
        Fill="{StaticResource HighlightGradient}" />
    
    
  2. Add the following Storyboard to the grid's resources, after the resources you have already added. This Storyboard animates the opacity of the rectangle you just added to the ControlTemplate to cause the effect to occur gradually. The Button control's logic begins this Storyboard when the user moves the mouse over the Button.

    cs

    <!--Add the Storyboards that specify states here.-->
    <Storyboard x:Key="MouseOver State">
        <DoubleAnimation Duration="0:0:0.1" 
             Storyboard.TargetName="Highlight" 
             Storyboard.TargetProperty="Opacity" To="1"  />
    </Storyboard>
    
    

Now when you run the application and move the mouse over a button, you will see its appearance slightly change.

To add the Pressed State to the ControlTemplate

  1. Add the following Storyboard to the grid's resources. This Storyboard causes the button to appear pressed by animating the Opacity of the Rectangle called Reflection from 1 to 0 and the Opacity of the rectangle called Highlight from 0 to 1.

    cs

    <Storyboard x:Key="Pressed State">
        <DoubleAnimation Duration="0:0:0.1" 
             Storyboard.TargetName="Reflection" 
             Storyboard.TargetProperty="Opacity" To="0"  />
    
        <DoubleAnimation Duration="0:0:0" 
            Storyboard.TargetName="Highlight" 
            Storyboard.TargetProperty="Opacity" To="1"  />
    </Storyboard>
    
    

Now when you click a button, it slightly changes its appearance.

To add the Disabled State to the ControlTemplate

  1. Add the following Rectangle to the ControlTemplate immediately after the ContentPresenter. This is a slightly translucent, white Rectangle that makes a button appear "grayed out." Like the Rectangle you added to add the MouseOver State, this rectangle's Opacity is set to 0 so the rectangle is completely transparent.

    cs

    <!--Rectangle used for the Disabled State-->
    <Rectangle x:Name="Disabled" 
        RadiusX="4" RadiusY="4" 
        Fill="{StaticResource DisabledBrush}"
        Opacity="0" IsHitTestVisible="false" />
    
    
  2. Add the following Storyboard to the grid's resources. The button calls this Storyboard when the IsEnabled property is set to false. The Storyboard sets the Opacity of the Rectangle you just added to 1. The Duration property of the DoubleAnimation is set to 0:0:0, so the Opacity changes from 0 to 1 immediately, instead of being animated. Even though the value of Opacity is not animated, the Button still uses a Storyboard to change the appearance of the Button when it is disabled so that it is the responsibility of the ControlTemplate to change button's appearance when it is disabled.

    cs

    <Storyboard x:Key="Disabled State">
        <DoubleAnimation Duration="0:0:0" 
            Storyboard.TargetName="Disabled" 
            Storyboard.TargetProperty="Opacity" To="1" />
    </Storyboard>
    
    
  3. Set the IsEnabled property of one of the buttons to false.

    cs

    <Button Content="Button 1" IsEnabled="False"
            Style="{StaticResource buttonStyle}" />
    
    

Now when you run the application, the disabled button looks lighter than the buttons that are enabled.

The last state to add to the ControlTemplate is the Normal State. You probably noticed that when you move the mouse over a button and then move the mouse away from the button, the highlight effect remains. The button begins the Normal state when the mouse moves outside of the button, so the Normal State should remove the highlight effect from the button.

To add the Normal State to the button

  1. Add the following Storyboard to the grid's resources. In addition to removing the highlight effect, the Storyboard also reinstantiates the reflection effect by setting the Opacity of Reflection to 1.

    cs

    <Storyboard x:Key="Normal State">
        <DoubleAnimation Duration="0:0:0.1" 
             Storyboard.TargetName="Highlight" 
             Storyboard.TargetProperty="Opacity" 
             To="0"  />
    
        <DoubleAnimation Duration="0:0:0.1" 
             Storyboard.TargetName="Reflection" 
             Storyboard.TargetProperty="Opacity" To="1"  />
    </Storyboard>
    
    

Now when you run the application, each button that uses your ControlTemplate has a distinct look when it is pressed, disabled, when the mouse is over it, and when it is in its normal state, just as the buttons that use the default ControlTemplate do.

As was mentioned earlier, the TemplateBinding Markup Extension enables you to set properties on each control and have those values used. For example, the Content property for each Button in your application is set to a different value. The ControlTemplate you created also binds the Background property of the Button, so you can change the color of each button and still use your custom ControlTemplate. The following example creates three buttons that use the custom ControlTemplate and gives each button a different color.

cs

<Button Content="Button 1" Style="{StaticResource buttonStyle}"
        Background="Red" />
<Button Content="Button 2" Style="{StaticResource buttonStyle}" 
        Background="Orchid"/>
<Button Content="Button 3" Style="{StaticResource buttonStyle}"
        Background="Gold" />

Many of the controls that ship with Silverlight use control templates and control contracts to specify their appearance. If there is a control that has the functionality you need, but you want to customize its appearance, consider creating a new ControlTemplate for the existing control. For information about the control contracts and templates for the controls that ship with Silverlight, see Control Styles and Templates. If you create a custom control, it is recommended that you use a ControlTemplate and provide a control contract so users of your control can customize its appearance. For more information, see Creating Custom Controls for Silverlight.

See Also