Developing a New Control Using VSM in Silverlight 3


Introduction

Silverlight  provides a Visual State Manager (or VSM) that manages different states as users interact with controls. For example, you might want to change the background of the Button as the user mouses over it and make it look slightly bigger or smaller when a user clicks it. All of this can be done using the Visual State Manager. The discussion that follows will walk through show how custom templates and VSM states can be created visually.

Concept

The developer usually does not think about a control element representation when it is underway. More important is to program logic of a control element. External representation is set later. As a general rule, a template (ControlTemplate) is formed to that end. However, any control element (even the simplest one) has a set of conditions that allows it to look differ. These conditions are named states. For example, an ordinary button can have several states - normal, pressed, hovered, etc. It is more logical when business logic of a control element informs about the current state, and the control element is in the process of representation changing. Work of VisualStateManager object is based on this approach.

VisualStateManager object allows determination of a state of a current control element. Usually the level of business logic does it within a control element. A control element internal event can be the reason (for example, press a button of a mouse or input text). It is necessary to attach VisualStateManager object to an internal control element within a template (ControlTemplate) to set representation. So, VisualStateManager object within a template will trace a current state automatically and execute transformations of user interface if there is need.

Creation of own control

VisualStateManager object is a link between business logic of a control element and its external representation. To connect VisualStateManager to a template of a control element it is necessary to define an attached property of VisualStateGroups. Within this property it is necessary to describe all states and transitions, and change the display of a control element appearance depending on a state. It is necessary to do it within business logic of a control element to change these states actually. Usage of GoToState method helps to do it. The name of the state is passed as parameters of this method. The state is set as a usual text line. It is possible to define a unique set of states for each control. Also, this method contains the third parameter "useTransitions"  which indicates whether it is necessary to use "Transitions" going to this state.

All standard control elements have a set of predetermined states. These conditions can be used developing a new control element template. The same approach can be used to create your own control elements. Let us create a new one and add a text property to it.

The definition of new control

public class VSMSampleControl : Control
{
      public static readonly DependencyProperty   TextProperty =
     DependencyProperty.Register(
"Text",typeof(string),typeof(VSMSampleControl), null);   

      public string Text

      {

            get

            {

                  return GetValue(TemplateProperty) as string;

            }

            set

            {

                  SetValue(TemplateProperty, value);

            }

      }

}

The definition of new control with switching methods

Now it is necessary to define time moments when the control element will switch its state. Here we create some methods which simulate state switching.

public class VSMSampleControl : Control

{

      public static readonly DependencyProperty TextProperty = 

            DependencyProperty.Register("Text", typeof(string), 

            typeof(VSMSampleControl), null);

      public string Text

      {

            get

            {

                  return GetValue(TemplateProperty) as string;

            }

            set

            {

                  SetValue(TemplateProperty, value);

            }

      }

 

      public void DoWork()

      {

            VisualStateManager.GoToState(this, "State1"true);

      }

 

      public void DoAnotherWork()

      {

            VisualStateManager.GoToState(this, "State2"true);

      }

 

      public void ClearState()

      {

            VisualStateManager.GoToState(this, "Normal"true);

      }

}

The describing of states for the control

TemplateVisualStateAttribute marks the description of states added for our control element.

[TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]

[TemplateVisualState(GroupName = "CommonStates", Name = "State1")]

[TemplateVisualState(GroupName = "CommonStates", Name = "State2")]

public class VSMSampleControl : Control

{

      public static readonly DependencyProperty TextProperty = 

            DependencyProperty.Register("Text", typeof(string), 

            typeof(VSMSampleControl), null);

      public string Text

      {

            get

            {

                  return GetValue(TemplateProperty) as string;

            }

            set

            {

                  SetValue(TemplateProperty, value);

            }

      }

 

      public void DoWork()

      {

            VisualStateManager.GoToState(this, "State1"true);

      }

 

      public void DoAnotherWork()

      {

            VisualStateManager.GoToState(this, "State2"true);

      }

 

      public void ClearState()

      {

            VisualStateManager.GoToState(this, "Normal"true);

      }

}

Now we have the control done

Using the control

VisualStateManager object will help to define a representation for each state. As an example we will display a text field with a name of the current state.
Besides, in a state "Normal" we will display the text from "Text" property. In this case the following template should be defined to the control element.

<myapp:VSMSampleControl x:Name="MyNewControl">

  <myapp:VSMSampleControl.Template>

    <ControlTemplate TargetType="myapp:VSMSampleControl">

      <Grid>

        <vsm:VisualStateManager.VisualStateGroups>

          <vsm:VisualStateGroup x:Name="CommonStates">

            <vsm:VisualState x:Name="Normal">

              <Storyboard>

                <DoubleAnimation To="1" Duration="0:00:00.5"     Storyboard.TargetName="NormalText" Storyboard.TargetProperty="Opacity"/>

                <DoubleAnimation To="0" Duration="0:00:00.5" 

                        Storyboard.TargetName="State1Text" Storyboard.TargetProperty="Opacity"/>

                <DoubleAnimation To="0" Duration="0:00:00.5" 

                        Storyboard.TargetName="State2Text" Storyboard.TargetProperty="Opacity"/>

              </Storyboard>

            </vsm:VisualState>

            <vsm:VisualState x:Name="State1">

              <Storyboard>

                <DoubleAnimation To="0" Duration="0:00:00.5"

Storyboard.TargetName="NormalText" Storyboard.TargetProperty="Opacity"/>

                <DoubleAnimation To="1" Duration="0:00:00.5"

Storyboard.TargetName="State1Text" Storyboard.TargetProperty="Opacity"/>

                <DoubleAnimation To="0" Duration="0:00:00.5"

Storyboard.TargetName="State2Text" Storyboard.TargetProperty="Opacity"/>

              </Storyboard>

            </vsm:VisualState>

            <vsm:VisualState x:Name="State2">

              <Storyboard>

                <DoubleAnimation To="0" Duration="0:00:00.5"

Storyboard.TargetName="NormalText" Storyboard.TargetProperty="Opacity"/>

                <DoubleAnimation To="0" Duration="0:00:00.5"

Storyboard.TargetName="State1Text" Storyboard.TargetProperty="Opacity"/>

                <DoubleAnimation To="1" Duration="0:00:00.5"

Storyboard.TargetName="State2Text" Storyboard.TargetProperty="Opacity"/>

              </Storyboard>

            </vsm:VisualState>

          </vsm:VisualStateGroup>

        </vsm:VisualStateManager.VisualStateGroups>

 

        <TextBlock x:Name="NormalText" Text="{TemplateBinding Text}"/>

        <TextBlock x:Name="State1Text" Text="State1" Opacity="0"/>

        <TextBlock x:Name="State2Text" Text="State2" Opacity="0"/>

      </Grid>

    </ControlTemplate>

  </myapp:VSMSampleControl.Template>

</myapp:VSMSampleControl>

Now if the object methods are called externally, the state changes. Changing a control element state is the reason of its appearance modification.

I think this article helps the reader understand the Importance of VSM and how to create own custom controls.
Happy Coding!

Up Next
    Ebook Download
    View all
    Learn
    View all