A few weeks ago in the Silverlight forums, a user asked how they could attach to the OnCompleted event of a Visual StateManager animation. At first, this sounds trivial, but the VisualStateManager does not directly expose a way to obtain a Storyboard. However, the VisualStateManager does return a list of the VisualStateGroups through the GetVisualStateGroups method. We can take advantage of this method to obtain a reference to the desired storyboard.
The FindStoryboard method defined in this helper class iterates over each of the VisualStateGroups until it finds a match, it then iterates over each Visual State in the group until it finds a match. The Storyboard for the matching visual state is then returned.
public class VisualStateManagerUtils
{
public static Storyboard FindStoryboard(FrameworkElement parent, string groupName, string stateName)
{
var vsgs = VisualStateManager.GetVisualStateGroups(parent);
foreach(VisualStateGroup vsg in vsgs)
{
if (vsg.Name != groupName)
continue;
foreach (VisualState vs in vsg.States)
{
if (vs.Name == stateName)
return vs.Storyboard;
}
}
return null;
}
}
Now, it’s a simple matter of calling our helper method and doing something useful with the Storyboard reference. For example, you could attach an event handler to the Completed event on the Storyboard.
public partial class Page : UserControl
{
public Page()
{
...
Storyboard sbHidden = VisualStateManagerUtils.FindStoryboard(LayoutRoot, "CommonStates", "Hidden");
sbHidden.Completed += new EventHandler(sbHidden_Completed);
}
void sbHidden_Completed(object sender, EventArgs e)
{
// Do something cool here. } ... ...
Update: had an excellent suggestion of making this method an extension method. The resulting syntax is much more concise and easier to use. With a few small changes, we can convert the helper method into an extension method.
public static class VisualStateManagerUtils
{
public static Storyboard FindStoryboard(this FrameworkElement parent, string groupName, string stateName)
{
var vsgs = VisualStateManager.GetVisualStateGroups(parent);
foreach (VisualStateGroup vsg in vsgs)
{
if (vsg.Name != groupName)
continue;
foreach (VisualState vs in vsg.States)
{
if (vs.Name == stateName)
return vs.Storyboard;
}
}
return null;
}
}
Now, it is much simpler to call the method
Storyboard sb = LayoutRoot.FindStoryboard("CommonStates", "Hidden");