Initializing the FROM value of a Silverlight 2 Animation

0 comments

Posted on 4th January 2009 by r2 in Silverlight |Silverlight 2

Recently, I was attempting to create my own popup dialog. I had specific requirements in mind for this dialog, including:

  1. I wanted to animate the Width/Height from 0×0 to the ActualWidth/ActualHeight
  2. I wanted to animate the Opacity from 0 to 1
  3. I wanted to animate the Top/Left properties from the point of mouse click (an icon
    in my case) to the center of the screen and back to the original point of mouse click
    when the user closed the dialog

All of these are fairly easy to accomplish using a Storyboard with an x:Name specified. In the case of my requirement #3 above, I merely needed to set the From value of my animation and call the Begin()method, like so (for purposes of illustration, I’ll confine this to the Canvas.Left property only…you can apply the same logic to the Canvas.Top property and it’s all in the accompanying project):

  1. <Storyboard x:Name="MoveDialogStoryboard">
  2.     <DoubleAnimation
  3.         x:Name="theDialogAnimation"
  4.         Storyboard.TargetName="theDialog"  
  5.         Storyboard.TargetProperty="(Canvas.Left)"
  6.         To="0" Duration="0:0:0.5" />
  7. </Storyboard>

 

  1. // this value would actually come from getting the mouse position via GetPosition()
  2. theDialogAnimation.From = 800;   
  3.  
  4. (MoveDialogStoryboard as Storyboard).Begin();

This all works as you would expect.  However, I really liked the idea of isolating all of my dialog states into one place and the Visual State Manager sounded like the perfect solution…that is, until I tried to set the FROM value programmatically and got hit with a NullReferenceException every time I tried to reference my named Animation.

I tried several things and ended up posting my issue to the Silverlight forum where Shawn Wildermuth was kind enough to offer a solution.  Shawn’s solution was to simply set the property to the value before calling GoToState() in my code. This made perfect sense and I ran off to give it a try.  It worked…once. My animation would fire once but then appear to be swallowed or ignored on subsequent clicks.  After further investigation, this turned out to be an artifact of the fact that I was never setting my VisualState back to the original state…something you don’t have to do when using a Storyboard outside of the Visual State Manager. So, that fixed that issue and Shawn’s solution worked.  However, I didn’t really see any way using this solution to animate back to the original point of mouse click.

Either way, it didn’t really satisfy my initial curiosity for WHY I couldn’t programmatically set the FROM value when my animation was inside a Visual State Manager, so I pressed on.  Thanks to a couple of blog posts (here and here) and some pretty ugly LINQ, I was finally able to programmatically set the FROM value and have it animate my dialog from the original point of mouse click with a Storyboard inside of a VisualState like so:

  1. public void ShowDialog(Point startingPosition)
  2. {
  3.      Storyboard storyboard = Utils.FindStoryboard(LayoutRoot, "DialogStates", "Open");
  4.      (storyboard.Children[0] as DoubleAnimation).From = startingPosition.X;
  5.      (storyboard.Children[1] as DoubleAnimation).From = startingPosition.Y;
  6.      VisualStateManager.GoToState(this, "Open", true);
  7. }
  8. // This is the definition of the extension method FindStoryboard() shown
  9. //    above (which is where the real work happens)
  10. public static Storyboard FindStoryboard(this FrameworkElement parent,
  11.                                         string groupName, string stateName)
  12. {
  13.      VisualState visualState = VisualStateManager.GetVisualStateGroups(parent)
  14.          .Cast<VisualStateGroup>().Where(group => group.Name == groupName)
  15.          .SingleOrDefault()
  16.          .States.Cast<VisualState>()
  17.          .Where(state => state.Name == stateName)
  18.          .SingleOrDefault();
  19.  
  20.      if (visualState != null)
  21.          return visualState.Storyboard;     
  22.  
  23.      return null;     
  24. }

All of that being said, I didn’t end up going this route nor do I recommend it. It’s way too brittle and just plain ugly in my opinion.  In the end, I chose to create my Open and Closed Storyboard animations for my popup dialog as standard Resource Storyboards where I don’t have to reset the State before calling and I don’t have to jump through hoops to set the value and, most importantly, I can set a name for the Animation and directly set the property value.  For the actual "modes" of my dialog (assuming there are multiple views for the dialog), I used the VSM to represent things like EntryView, EditView, etc.  I think this offers a much cleaner solution.

I’ve put together a quick demo to illustrate both calling the Storyboard directly and using a Storyboard in a VSM. 

The live version is here and the project source is available here.

No comments yet.

Sorry, the comment form is closed at this time.