Expression Blend, Silverlight

Using Visual States in custom controls in Silverlight

Intro

Visual States are an easy way to change the looks of your controls based on certain states. This state can be something like a mouse hover, some invalid state or any state you need in a control.

For this tutorial I chose a traffic light control that can be one of four states. Green, Orange, Red and Inconclusive (blinking orange). In the end I show you how to use behaviors on buttons to set the state of the traffic light. This makes the use of visual states perfectly suitable for use in MVVM projects.

Part 1 – Setting up the project

Start by creating a new Silverlight project in Visual Studio 2010 and add a new Item to the project by right clicking on the project in the Solution window and click Add –> New Item… or hit Ctrl+Shift+A.

image

Select the Silverlight Templated Control item template from the list of Silverlight templates

image

Add a folder named Themes to the Silverlight project. Add a ResourceDictionary to the project by using the Add->New Item… on the project folder and name this dictionary Generic.xaml. Add the following style to the dictionary:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:VsmDemo="clr-namespace:VsmDemo">
  <Style TargetType="VsmDemo:TrafficLight">
    <Style.Setters>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate/>
        </Setter.Value> 
      </Setter>
    </Style.Setters>
  </Style>
</ResourceDictionary>

Add a reference to the dictionary in App.xaml:

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="Themes/generic.xaml"/>
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
 </Application.Resources>

Build the solution to see I there aren’t any mistakes so far.

Part 2 – Drawing in Blend

Start Expression Blend and load the solution.

The template is still empty. To start editing the style for the TrafficLight control, go to the Resource pane, look for the generic.xaml entry, open it and click on the button on the right of the style entry.

image

To edit the template in the style, right click the style in the Objects and Timeline pane and select Edit Template –> Edit Current.

image

Now draw a traffic light like this.

image

If you don’t like/want/know how to draw the light, just copy-past the xaml below into the xaml editor. It’s basically just a border with a grid containing three ellipses.

<ControlTemplate>
  <Border d:DesignHeight="200.00" BorderBrush="#FFCACACA"
          BorderThickness="3"
          Background="#FF333333" CornerRadius="40"
          d:DesignWidth="110">
    <Grid >
      <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition Height="30"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
      <ColumnDefinition Width="30"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="30"/>
      </Grid.ColumnDefinitions>
      <Ellipse x:Name="GreenLight"
               Grid.Column="1" Grid.Row="1"
               Fill="Black" Margin="2"
               Stretch="Uniform"/>
      <Ellipse x:Name="OrangeLight"
               Grid.Column="1" Grid.Row="2"
               Fill="Black" Margin="2" 
               Stretch="Uniform"/>
      <Ellipse x:Name="RedLight"
               Grid.Column="1" Grid.Row="3"
               Fill="Black" Margin="2" 
               Stretch="Uniform"/>
    </Grid>
  </Border>
</ControlTemplate>

Instantly the traffic light should show up in the designer.

Time to save your work again. Switch back to Visual Studio, and reload the files if you’re asked to.

Part 3 – Code… Finally

The selection of the different states will be based on an enum. Add a new enum to the Silverlight project.

public enum TrafficLightStates
{
    Inconclusive = 0,
    Green = 1,
    Orange = 2,
    Red = 3
}

The state of the traffic light will be stored in the control in a dependency property. The property will be set to TrafficLightStates.Green by default. A method must be called when the property changes to notify the state manager to change the state of the traffic light.

public TrafficLightStates State
{
    get { return (TrafficLightStates)GetValue(StateProperty); }
    set { SetValue(StateProperty, value); }
}

public static readonly DependencyProperty StateProperty =
    DependencyProperty.Register(
           "State", 
           typeof(TrafficLightStates),
           typeof(TrafficLight),
           new PropertyMetadata(TrafficLightStates.Green,
           OnLightStateChanged)
    );

To get the code to build, add a basic implementation of the OnLightStateChanged method. The OnLightStateChanged method calls the non-static SetState method. This way there will be only one method in which the visual state will be changed. The SetState method is empty for now.

private static void OnLightStateChanged(DependencyObject d,
                 DependencyPropertyChangedEventArgs e)
{
    ((TrafficLight)d).SetState();
}

private void SetState(){}

To make the four different visual states known to the application you’ll have to use the TemplateVisualState attribute. This attribute takes the name of the state and the name of the group the visual state belongs to. Add an instance of the TemplateVisualState attribute for each of the four states to the TrafficLight class.

[TemplateVisualState(Name = "Green", 
                     GroupName = "TrafficLightStates")]
[TemplateVisualState(Name = "Orange", 
                     GroupName = "TrafficLightStates")]
[TemplateVisualState(Name = "Red", 
                     GroupName = "TrafficLightStates")]
[TemplateVisualState(Name = "Inconclusive", 
                     GroupName = "TrafficLightStates")]

Now the states are named it is time to set the visual states based on the state of the traffic light. This is done by a simple switch block in the SetState method.

Changing the visual state of a control is done though a static method on the VisualStateManager class. Calling the GotoState method with the name will change the current visual state to a new one. Any transitions will be played automatically, if the useTransitions parameter is set to true. The GotoState method also needs a control of which the visual state needs to be sit. For this example we can just pass this to the method.

private void SetState()
{
    switch (State)
    {
        case TrafficLightStates.Green:
            VisualStateManager.GoToState(this, "Green", true);
            break;
        case TrafficLightStates.Orange:
            VisualStateManager.GoToState(this, "Orange", true);
            break;
        case TrafficLightStates.Red:
            VisualStateManager.GoToState(this, "Red", true);
            break;
        default:
            VisualStateManager.GoToState(this, "Inconclusive", true);
            break;
    }
}

The last thing that has to be done in code is setting the initial state by calling the SetState method once. You can override the OnApplyTemplate method for that.

public override void OnApplyTemplate()
{
    SetState();
    base.OnApplyTemplate();
}

Build to see if there are any mistakes made in the code… After that, switch back to Expression Blend.

Part 4 – Visual States

The last part of this tutorial will take place in Expression Blend. Every state of the traffic light has to have a corresponding visual state. The green light must be green, etc.

Go to the template of the TrafficLight control as shown before and open the States pane. Click on the Add State Group button on the right side of the pane.

image

Rename the VisualStateGroup group to TrafficLightStates.

image

Add four states to the group by clicking the Add state button right of the TrafficLightStates group header four times.

image

image

Rename the states to Green, Orange, Red and Inconclusive.

image

With the Green visual state and the GreenLight Ellipse selected, change the fill of the ellipse to green, like #FF2AFF00. Notice the red border around the drawing area indicating changes to the properties will be recorded. A small red light with a caption is shown at the top of the drawing area letting you know which state is recorded too, in this case “Green state recording is on”

image image
image image
image  

This process needs to be repeated for the orange and red ellipses too. In the example I’ve used #FFFFAA00 for the orange ellipse and #FFFF0000 for the red one.

The last state, Inconclusive, needs an animation so the orange light blinks. Select the Inconclusive visual state on the States pane. Show the animation timeline by clicking on the Show Timeline button on the Objects and Timeline pane.

image

Move the time slider to 0,5 seconds. Change the color to Orange (#FFFFAA00). Move the time slider to 1 second and change the color to black.

Click on the first key frame and set its easing function to cubic out.

image

Repeat the same for the second key frame. Set its easing function to cubic out also.

Select the storyboard by clicking on Inconclusive in the Objects and Timeline pane.

image

In the properties pane, set the Repeat Behavior of the storyboard to Forever.

image

That’s it for the visual states. Try compiling the application.

Part 5 – Testing the control

Open MainPage.xaml in Expression Blend.

Add the TrafficLight control to the LayoutRoot grid and give it a width of 110 and a height of 200.

To navigate to the four different states of the traffic light, add four buttons to the LayoutRoot grid and set their content to Green, Orange, Red and Inconclusive. Like this:

image

To change to State property of the traffic light you can use a ChangePropertyAction. You can find this in the Behaviors group on the Assets pane.

image

Drag-‘n-drop this action on the first button.

image

Select the Action in the Objects and Timeline pane. To set the TargetObject of action to the traffic light just click on the Artboard element picker and than on the traffic light.

image

Select State from the PropertyName combobox.

image

Select Green from the Value combobox.

image

Repeat the same process for the other three buttons. You can clone the behavior from element to element by selecting the behavior and drag-‘n-drop it to another element while holding down the Ctrl key.

Done!

Hit F5 to admire your work Smile 

You can download the source of the project here.

If you run into any issues or if you have comments or questions, please let me know. To be the first to know if there’s a new post on my blog, please subscribe to the RSS feed or follow me (@sorskoot) on twitter.

Angular 2 and NodeJS - The Practical Guide to MEAN Stack 2.0

The Complete Web Developer Course 2.0

4 comments

  1. Hi

    This is great at the fixed size but if you try and resize the control the structure goes to pieces. Any ideas on how this can be resized correctly

    thanks

  2. If the grid rows and columns are defined correctly the traffic light should resize fine. I’ve set the scale of the three circles to be uniform. You could change this to Fill to make the scaling fill the entire space.

  3. Hi Timmy,
    thanx for the great example. Is it possible to extend it a bit so you show how to manipulate the state in code-behind? Thanx in advance.

  4. Thanks for the great example! I was missing the VERY important part about settings the initial states in the OnApplyTemplate() method. This was resulting in my controls visual state not obeying their initial bindings but still properly animating between states when I interacted with them.

Leave a Reply

Seo wordpress plugin by www.seowizard.org.
%d bloggers like this: