Silverlight 3 Flip TriggerAction
July 30, 2009 @ 11:26 am in Microsoft, Silverlight
In this blog post I am going to create a TriggerAction which makes it incredibly simple to take two UIElements and compose them into a panel which can be flipped. I have demonstrated the technique many times, but here it is all packaged up in a nice reusable class.
The following is the code I used to create by Flip TriggerAction.
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Interactivity;
using System.ComponentModel;
namespace FlipTriggerAction
{
public enum RotationDirection
{
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop
}
public class Flip : TriggerAction<FrameworkElement>
{
public static readonly DependencyProperty FrontElementNameProperty =
DependencyProperty.Register("FrontElementName", typeof(string),
typeof(Flip), new PropertyMetadata(null));
[Category("Flip Properties")]
public string FrontElementName { get; set; }
public static readonly DependencyProperty BackElementNameProperty =
DependencyProperty.Register("BackElementName", typeof(string),
typeof(Flip), new PropertyMetadata(null));
[Category("Flip Properties")]
public string BackElementName { get; set; }
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(Duration),
typeof(Flip), new PropertyMetadata(null));
[Category("Animation Properties")]
public Duration Duration { get; set; }
public static readonly DependencyProperty RotationProperty =
DependencyProperty.Register("Rotation", typeof(RotationDirection),
typeof(Flip), new PropertyMetadata(RotationDirection.LeftToRight));
[Category("Animation Properties")]
public RotationDirection Rotation { get; set; }
private readonly Storyboard _sbF = new Storyboard();
private readonly Storyboard _sbB = new Storyboard();
private bool _forward = true;
protected override void Invoke(object parameter)
{
if (AssociatedObject.Projection == null)
{
FrameworkElement parent = AssociatedObject as FrameworkElement;
UIElement f = null;
UIElement b = null;
if (parent != null)
{
f = parent.FindName(FrontElementName) as UIElement;
if (f != null)
{
PlaneProjection ppFront = new PlaneProjection { CenterOfRotationY = .51 };
f.Projection = ppFront;
f.RenderTransformOrigin = new Point(.5, .5);
}
b = parent.FindName(BackElementName) as UIElement;
if (b != null)
{
PlaneProjection ppBack = new PlaneProjection { CenterOfRotationY = .51, RotationY = 180.0 };
b.Projection = ppBack;
b.RenderTransformOrigin = new Point(.5, .5);
b.Opacity = 0.0;
}
}
PlaneProjection pp = new PlaneProjection { CenterOfRotationY = .51 };
AssociatedObject.RenderTransformOrigin = new Point(.5, .5);
AssociatedObject.Projection = pp;
double to = 180.0;
double from = 0.0;
string property = "RotationY";
switch (Rotation)
{
case RotationDirection.RightToLeft:
to = 0.0;
from = 180.0;
break;
case RotationDirection.TopToBottom:
property = "RotationX";
break;
case RotationDirection.BottomToTop:
to = 0.0;
from = 180.0;
property = "RotationX";
break;
}
_sbF.Duration = Duration;
_sbB.Duration = Duration;
_sbF.Children.Add(CreateDoubleAnimation(pp, property, from, to, true));
_sbB.Children.Add(CreateDoubleAnimation(pp, property, to, from, true));
_sbF.Children.Add(CreateDoubleAnimation(f, "Opacity", 1.0, 0.0, false));
_sbB.Children.Add(CreateDoubleAnimation(f, "Opacity", 0.0, 1.0, false));
_sbF.Children.Add(CreateDoubleAnimation(b, "Opacity", 0.0, 1.0, false));
_sbB.Children.Add(CreateDoubleAnimation(b, "Opacity", 1.0, 0.0, false));
}
if (_forward)
{
_sbF.Begin();
_forward = false;
}
else
{
_sbB.Begin();
_forward = true;
}
}
private static DoubleAnimation CreateDoubleAnimation(DependencyObject element, string property, double from,
double to, bool addEasing)
{
DoubleAnimation da = new DoubleAnimation();
da.To = to;
da.From = from;
if (addEasing)
da.EasingFunction = new PowerEase() { EasingMode = EasingMode.EaseOut, Power = 3 };
Storyboard.SetTargetProperty(da, new PropertyPath(property));
Storyboard.SetTarget(da, element);
return da;
}
}
}Once you have the class constructed you can add the trigger to your UIElement in Blend. Clicking on the trigger in the Object & Timeline tab will allow you to configure each of the the properties you have defined in your class.
Code: FlipTriggerAction.zip
Tagged as 

July 31st, 2009 at 8:43 am
Can this be used to say, click on a small grid control and have it flip over into a 3D column chart displaying the numbers in the grid?
July 31st, 2009 at 9:41 am
Eric, When I first created the Trigger it was a for a dashboard that when you clicked on a bar graph (Visifire graph) it fliped over to another view (Pie chart, etc).
August 6th, 2009 at 8:54 am
This is great, Joel. I have a question though: How can I invoke the action from C# code behind? Cannot call Invoke() directly…
The reason I want to do this is I want to have more than one source elements to trigger the same Flip Action. Can it be done?
August 6th, 2009 at 10:08 am
Jason, I wrote this action specifically to be invoked by clicking on the container panel. If you want the behavior you describe, you need to change it to a TargetedTriggerAction. This will allow you to have a button call the invoke but make sure the animation and projection is added to a specific Panel (what is referencedi nthe TargetName). I can create this example and post it later today.
August 7th, 2009 at 9:23 am
Thanks a lot Joel. I am new to Silverlight behavior stuff. An example will be great!
Besides, I found a small problem when I try to put some Textbox and ScrollViewer controls on both panels. It turns out that the Front Panel block the Back Panel’s controls (or the other way around depends on which element is in the front in XAML), even the Fron Panel’s Opacity is set to 0 already.
The Textbox on the other panel cannot receive focus because of this. I modified the codes to make flip-to-back panel visibility=collapsed when StoryBoard competed, and it solved the problem. I also moved those basic setting codes to OnAttached() event and only run StoryBoard.Begin() in Invoke().
I will try to post the modified codes somewhere since my current Blog is written in Chinese… :)
Thanks a lot for the inspiration and sample codes!
October 2nd, 2009 at 7:34 am
Joel, I have a question. Your codes are no doubt great but it is not doing exactly what I wish to. Currently, I implement your codes to do on MouseEnter flip button. I have included MouseLeave flip event to flip the button back to its original state but to no avail. Could you give me a hand here? I am pretty new to Silverlight and your help is greatly appreciated. Thanks in advance.