A bit more(er) than meets the eye [Easily animate and update LayoutTransformer with AnimationMediator!]
Yesterday I posted the code for AnimationMediator, a simple Mediator class to make it easy to animate the Transitions of a LayoutTransformer. Today, Silverlight Toolkit teammate Ted Glaza asked why the LayoutTransformerName
property was present and I said that while I'd really wanted to use Binding+ElementName
with the LayoutTransformer
property, it didn't work for me when I tried. Ted kindly pointed out that in order for that to succeed, the LayoutTransformer
property needed to be a DependencyProperty - and I'd used a simple CLR property instead. :(
Aside: What happened is that I originally used a DependencyProperty, convinced myself it wasn't working, then simplified to a plain CLR property. But I obviously did something wrong along the way, because the scenario I wanted to enable works great once LayoutTransformer is a DependencyProperty.
So I've updated the implementation of AnimationMediator
accordingly; you can get the new version by downloading the source code again - or from below. And with this update there's no need to use the LayoutTransformerName
property on Silverlight 3 (in fact, I've removed it from the code!) - so the scenario from last time simplifies to the following:
<local:AnimationMediator x:Name="RotationMediator" LayoutTransformer="{Binding ElementName=ButtonTransformer}" AnimationValue="{Binding Angle, ElementName=Rotation, Mode=TwoWay}"/>
Aside: Binding's ElementName property isn't present on Silverlight 2, so theLayoutTransformerName
property is still relevant on that platform. Therefore, I have not changed theAnimationMediator
implementation I originally included in the text of yesterday's post.
[Click here to download the complete source code for the AnimationMediator sample application.]
As long as I was updating the sample application, I wanted to take the opportunity to show off two other things as well. The first is that AnimationMediator
is good for more than just Storyboards
- you can also use it with XAML-only bindings to other UI elements! In this case, I've added a TextBlock
and two Slider
s and hooked those Slider
s up to the ScaleTransform
of a LayoutTransformer
for the text. As you'll see if you run the demo, it works just like you'd expect and it's 100% XAML, no code. :) The second thing I wanted to show is a proof of my claim that you can use two AnimationMediators
with the same LayoutTransformer
- which I do here.
This is what the XAML for the new scaling scenario looks like:
<!-- Applies the LayoutTransform for TextBlock --> <layoutToolkit:LayoutTransformer x:Name="TextTransformer"> <!-- A scale transformation--> <layoutToolkit:LayoutTransformer.LayoutTransform> <ScaleTransform x:Name="Scale"/> </layoutToolkit:LayoutTransformer.LayoutTransform> <!-- Text being scaled --> <TextBlock Text="Scale Me!" FontSize="50" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/> </layoutToolkit:LayoutTransformer> <!-- An AnimationMediator for each Slider Binding --> <local:AnimationMediator x:Name="ScaleXMediator" LayoutTransformer="{Binding ElementName=TextTransformer}" AnimationValue="{Binding ScaleX, ElementName=Scale, Mode=TwoWay}"/> <local:AnimationMediator x:Name="ScaleYMediator" LayoutTransformer="{Binding ElementName=TextTransformer}" AnimationValue="{Binding ScaleY, ElementName=Scale, Mode=TwoWay}"/> <!-- A Slider for X and Y --> <Slider Maximum="5" Value="{Binding AnimationValue, ElementName=ScaleXMediator, Mode=TwoWay}" Grid.Row="1"/> <Slider Maximum="5" Value="{Binding AnimationValue, ElementName=ScaleYMediator, Mode=TwoWay}" Grid.Row="2"/>
So thanks for pointing this out, Ted - AnimationMediator
is now just about as simple as can be!
PS - Here's the updated implementation of AnimationMediator:
/// <summary> /// Class that acts as a Mediator between a Storyboard animation and a /// Transform used by the Silverlight Toolkit's LayoutTransformer. /// </summary> /// <remarks> /// Works around an issue with the Silverlight platform where changes to /// properties of child Transforms assigned to a Transform property do not /// trigger the top-level property changed handler (as on WPF). /// </remarks> public class AnimationMediator : FrameworkElement { /// <summary> /// Gets or sets a reference to the LayoutTransformer to update. /// </summary> public LayoutTransformer LayoutTransformer { get { return (LayoutTransformer)GetValue(LayoutTransformerProperty); } set { SetValue(LayoutTransformerProperty, value); } } public static readonly DependencyProperty LayoutTransformerProperty = DependencyProperty.Register( "LayoutTransformer", typeof(LayoutTransformer), typeof(AnimationMediator), new PropertyMetadata(LayoutTransformerPropertyChanged)); private static void LayoutTransformerPropertyChanged( DependencyObject o, DependencyPropertyChangedEventArgs e) { var layoutTransformer = (LayoutTransformer)(e.NewValue); if (null != layoutTransformer) { // Update now to be safe layoutTransformer.ApplyLayoutTransform(); } } /// <summary> /// Gets or sets the value being animated. /// </summary> public double AnimationValue { get { return (double)GetValue(AnimationValueProperty); } set { SetValue(AnimationValueProperty, value); } } public static readonly DependencyProperty AnimationValueProperty = DependencyProperty.Register( "AnimationValue", typeof(double), typeof(AnimationMediator), new PropertyMetadata(AnimationValuePropertyChanged)); private static void AnimationValuePropertyChanged( DependencyObject o, DependencyPropertyChangedEventArgs e) { ((AnimationMediator)o).AnimationValuePropertyChanged(); } private void AnimationValuePropertyChanged() { if (null == LayoutTransformer) { throw new InvalidOperationException( "AnimationMediator's LayoutTransformer property must not be null."); } // The Transform hasn't been updated yet; schedule an update to run after it has Dispatcher.BeginInvoke(() => LayoutTransformer.ApplyLayoutTransform()); } }