This is the third post in a four part series on Silverlight 2’s Parts & States control model.
Last time, you learned how to reskin an existing control using VisualStateManager. In this post, you’ll see how to build up a Parts & States-based custom control. We’ll also explore how you can create more sophisticated visual transitions.
We’ve saw it briefly in the last post, but let’s formally introduce VisualStateManager.
VisualStateManager is the class responsible for control visual state management. The “visual” modifier in that sentence is important – the control logic remains in charge of the logical state machine.
VSM exposes two main pieces of PME:
- a VisualStateGroups attached property
- This property is set on the control template’s root visual and contains all the visual states & transitions for that skin
- a static GoToState() method
- This method causes VisualStateManager to transition the control’s visuals from one visual state to another.
Last time, we concentrated on the VisualStateGroups property in XAML. Today, we’ll dig into how the control code leverages that GoToState() method.
The custom control that we’ll be looking at today is a simple WeatherControl. The shell of the control code can be found below. (Note: For readability, I’ve collapsed some of the code snippets. You can find the full sample code here.)
You can see that our WeatherControl…
- is a custom control, deriving from Control.
- defines its own built-in style, as indicated by the DefaultStyleKey.
- has 3 public dependency properties:
In order to make our WeatherControl skinnable with VSM, we need to:
- define a control contract
- discover & manipulate parts
- wire up appropriate state changes using VisualStateManager
Here we go!
Defining the Control Contract
The control code is responsible for documenting the control contract. This means it should declare any and all expected Parts and States. This is done using class level metadata:
In the above snippet, there are two attribute classes:
- Specifies the name of the part & expected type
- Specifies the name of the state & its associated state group
This metadata is not used by the runtime. However, it is leveraged by tools like Expression Blend for their skinning support.
These attributes on the WeatherControl give rise to this control bill of materials:
Now, let’s see how the control code manipulates Parts.
Parts are named elements in the template and need to be manually discovered by the control code. This is done in the OnApplyTemplate() virtual, which is called whenever a new template is applied.
To find a named element inside of the template, you use the GetTemplateChild() helper method.
In the above example, we discover the “Core” part, which we will use to determine when the control should go into the MouseOver or Pressed states. Note that the setter logic is resilient to the Core part not being in the template. This is important, because a control needs to be robust enough to handle a part that is missing or not yet been added.
Initiating State Changes
The control code is responsible for telling VisualStateManager when a visual state change should occur. Therefore, it must maintain the logical state machine that is associated with the visual state machine.
All of Silverlight 2’s built-in controls create a simple helper method to assist with the state changes. We recommend that you follow a similar pattern:
The GoToState helper method contains a series of if statements which determine the current visual states. It then tells VisualStateManager to initiate the appropriate state change. This is done with the static public static bool VisualStateManager.GoToState(Control control, string stateName, bool useTransitions) method.
As you can see, this method…
- has 3 parameters:
- control: instance of the control
- stateName: name of the visual state to go to
- usetTransitions: flag to determine whether transitions should be run in this state change
- returns a bool
- It returns true if the state name was found and false otherwise.
- is a no op if…
- the control was already in the passed in visual state
- the visual state cannot be found
Most control authors will call their GoToState() helper in 3 places:
- OnApplyTemplate() with no transitions.
- When the control first appears, we generally want it to just appear in the appropriate state, and not transition into it.
- In certain property change notification handlers
- In certain event handlers
For our WeatherControl, we add these calls:
For the WeatherControl, we need to initiated state changes when:
- the template is first applied
- the Condition property is changed
- mouse events are raised from the Core part
Adding a Built-In Style
So now we have our control logic!
I’ve cooked up a very fun (if I do say so myself) ControlTemplate to show off our WeatherControl. The “fun” makes the template a bit long, however. Here’s the editted version:
As you can see from this ControlTemplate, I’ve:
- defined all 7 VisualStates.
- leveraged storyboard resources to define the state storyboards
- supplied a default VisualTransition for both the CommonStates and WeatherStates
- specified VisualTransitionss for certain state changes in the CommonStates
Let’s run it!
Adding Specialized Transitions
The generated transitions work well for our WeatherControl. However, being ambitious, let’s add more customized visual transitions between certain state changes.
Here’s our skin in the different weather states:
When our control goes from Sunny to PartlyCloudy, we don’t want the cloud to to just gradually animate in. Instead, we’d like it to zoom from the left.
In order to create a custom transition like this, you can specify an explicit transition storyboard:
Now, when VisualStateManager is generating transitions animations for the Sunny to PartlyCloudy state change, it will no longer generate animation for the BottomCloud’s opacity. It will just run the explicit transition storyboard with its two double animations.
To better understand stand how generated transition animations interact with explicit transitions storyboards, let’s look at an example:
Here, we have two visual states: Foo & Bar. They each animate a different (partially overlapping) set of properties.
How do the the transition animations get built up?
- VSM will generate transition animations for properties A, C, and D.
- A, C, and D are animated in one or both of the two states, AND are not animated in the explicit VisualTransition.Storyboard storyboard.
- VSM will run the explicit transition storyboard to transition properties B, E, and G
- B, E, and G are animated by VisualTransitoin.Storyboard. VSM does not need to generate transition animations for these properties.
- VSM will not animate the transition of property F.
- F is animated with an ObjectAnimation in the Foo & Bar states. It’s not possible for VSM to programmatically generate a linear transition animation for an ObjectAnimation. Therefore, property F will simply snap into its Bar value after the transition animations have run.
Going back to our WeatherControl, I’ve also added explicit transitions for Sunny->PartlyCloudy, Sunny->Cloudy, and PartlyCloudy->Cloudy.
So that’s how to build up a Parts & States-based custom control using VisualStateManager. I hope you also enjoyed those custom explicit transitions.
Next time, in this series final post, we’ll give some general recommendations on how to use the Parts & States Model. You’ll also learn about some of our future plans for the Parts & States Model in Silverlight as well as Windows Presentation Foundation!