Endjin - Home

Where do my Visual States come from in Blend?

by Matthew Adams

This blog was prompted by a question from fellow endjineer and all-round genius, Ian Griffiths. He wanted to know how Expression Blend managed to find the Visual States exposed by the standard controls, and how you might, therefore, do the same job in your own code.

(If you want to read up a bit about the visual state manager, Ian has a great tutorial here. You can get a more in-depth view from the Pluralsight On Demand Silverlight courses.)

In Silverlight this is fairly straightforward.

Here are the Visual States for a CheckBox, as displayed in Blend:

image

And here is the corresponding code for a CheckBox as provided by Reflector:

[TemplateVisualState(Name="MouseOver", GroupName="CommonStates"), 
 TemplateVisualState(Name="Normal", GroupName="CommonStates"), 
 TemplateVisualState(Name="Checked", GroupName="CheckStates"), 
 TemplateVisualState(Name="InvalidFocused", GroupName="ValidationStates"), 
 TemplateVisualState(Name="Disabled", GroupName="CommonStates"), 
 TemplateVisualState(Name="Unfocused", GroupName="FocusStates"), 
 TemplateVisualState(Name="Focused", GroupName="FocusStates"), 
 TemplateVisualState(Name="Pressed", GroupName="CommonStates"), 
 TemplateVisualState(Name="Unchecked", GroupName="CheckStates"), 
 TemplateVisualState(Name="Indeterminate", GroupName="CheckStates"), 
 TemplateVisualState(Name="Valid", GroupName="ValidationStates"), 
 TemplateVisualState(Name="InvalidUnfocused", GroupName="ValidationStates")]
public class CheckBox : ToggleButton
{
}

All those TemplateVisualState attributes give Blend the information it needs. It can just enumerate the metadata for the control, and present the UI.

How about WPF?

Well, Blend looks pretty much exactly the same:

image

But if we look at the code, there don’t seem to be any of those visual state attributes.

[DefaultEvent("CheckStateChanged"), 
 Localizability(LocalizationCategory.CheckBox)]
public class CheckBox : ToggleButton
{
}

This is a shame, because the WPF framework does define the attribute (and you should use it on your own controls in exactly the same way as you would in Silverlight). If the standard controls don’t make use of it, though, how is Blend discovering these states, and building the UI?

A bit of rummaging around in the guts of Blend reveals an assembly called Microsoft.Expression.DesignSurface. This contains a type called DesignSurfaceMetadata, which itself contains a method called CreatePresentationFrameworkAssemblyInformation(). As the name sort-of implies, this adds additional metadata to the standard controls in the WPF presentation framework – specifically (but not exclusively) the missing TemplateVisualState attributes.

Aha! I thought; this is it. While the relevant Blend view model just enumerates those attributes to discover what to show in the UI, the infrastructure has previously added extra (hard-coded) attributes so it can do its job. It looks like a combination of doing it nicely in the view model layer, with some groinky hard-coding to work-around the lack of attributes on the relevant controls.

Turns out that’s nearly but not quite the case. If you look at the implementation of that method, it doesn’t fully cover all the standard controls.

Armed with this information, Ian had a bit more of a dig around, and found another suspect, in another assembly: Microsoft.Expression.Platform.WPF. There’s a class in there called CommonAttributeTableBuilder which does exactly the same job, but rather more comprehensively. It derives from a type called AttributeTableBuilder, which is a part of the standard metadata extensibility framework.

Either way, the conclusion is the same. You just have to know what states are exposed by the WPF standard controls; they don’t participate in the built-in discovery mechanism.

If you do need the same kind of discovery model as Blend, then the approach it has taken is a good one: add the additional metadata yourself somewhere in your bootstrapping process, then enumerate that metadata in your view model. That way, if a future version of WPF includes the relevant attributes, then you simply need to remove your hacky hard-coded extra metadata and your view model will continue to work correctly.

That’s not quite the end of the story, though. Intriguingly, VS2010 ships a version of the Microsoft.Expression.Platform.WPF assembly. An old version, but a version nonetheless. If that’s the case, then maybe we can use (via reflection) that internal CommonAttributeTableBuilder class to add the attributes for us, without taking a dependency on Blend? Maybe?

Turns out that the answer is again, no. The type isn’t present in the version that ships with VS2010. We still have to do it ourselves.

What about VS2010SP1 Beta? Yes! They have updated the version of the assembly. It isn’t one you can ship with your application, but you might reasonably expect it to be there if SP1 is present. Sadly, the WPF controls themselves haven’t been updated with the relevant attributes, though, and the metadata class is not publically available to us.

The conclusion, then is:

1) Annotate your own controls with the TemplateVisualStateAttribute if you make use of visual states. For an “industrial strength” control library, you can do that in an external assembly using the same metadata extension mechanism that Blend uses, to minimize the size of your control assembly. For a one-off control, you might not want to bother, and just annotate the control itself.

2) If you need to programmatically enumerate the visual states for a control, look for those attributes

3) If you need to do that for the standard Silverlight controls, that’s fine – they add the relevant attributes.

4) If you need to do that for WPF, then you will need to hard-code a solution. The Blend approach of extending the metadata (effectively adding the attributes dynamically) is a good one, so your enumeration mechanism doesn’t need special case code. You may (or may not) be able to use the internal CommonAttributeTableBuilder class in Microsoft.Expression.Platform.WPF to help you. At the very least, you can look at in Reflector it for inspiration.

Matthew Adams on Twitter

About the author

Matthew was CTO of a venture-backed technology start-up in the UK & US for 10 years, and is now a Founder of Endjin Ltd, which provides technology strategy, experience and development services to its clients who are seeking to take advantage of Microsoft Azure and the Cloud. You can follow Matthew on twitter.