Why isn't my windows phone silverlight rotation animation working? - silverlight

I've got this in my XAML:
<Grid.Resources>
<Storyboard x:Name="Storyboard_Animation">
<DoubleAnimation
Storyboard.TargetName="button_Submit"
Storyboard.TargetProperty="Angle"
From="0"
To="360"
Duration="0:0:1"></DoubleAnimation>
</Storyboard>
</Grid.Resources>
I have the button in the same Grid:
<Button Grid.Row="0" Grid.Column="1" Content="Submit" Margin="0" Name="button_Submit" Click="button_Submit_Click">
<Button.Template>
<ControlTemplate>
<Image Source="Images/buttonImage.png"></Image>
</ControlTemplate>
</Button.Template>
<Button.RenderTransform>
<RotateTransform></RotateTransform>
</Button.RenderTransform>
</Button>
I have this in my click method:
private void button_Submit_Click(object sender, RoutedEventArgs e)
{
Storyboard_Animation.Begin();
}
When I click on my button I get the error:
Cannot resolve TargetProperty Angle on specified object.
But, I have no idea what I'm supposed to use other than Angle.
I have this other piece of code that works fine:
private void RotateStar()
{
button_Submit.RenderTransformOrigin = new Point(0.5, 0.5);
button_Submit.RenderTransform = new RotateTransform();
DoubleAnimation da = new DoubleAnimation
{
From = 0,
To = 360,
Duration = TimeSpan.FromSeconds(0.3)
};
Storyboard.SetTarget(da, button_Submit.RenderTransform);
Storyboard.SetTargetProperty(da, new PropertyPath(RotateTransform.AngleProperty));
Storyboard sb = new Storyboard();
sb.Children.Add(da);
sb.Begin();
}
I'd like to put the storyboard in the XAML instead of in code. What do I need to add/change in my XAML version so that it works like the code version?

Try this:
<Grid.Resources>
<Storyboard x:Name="Storyboard_Animation">
<DoubleAnimation
Storyboard.TargetName="button_Submit"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:1">
</DoubleAnimation>
</Storyboard>
</Grid.Resources>

Your problem is in incorrect using "TargetProperty". Button doesn't have Angle property, you should use it for RenderTransform.
Such this:
<Storyboard x:Name="Storyboard_Animation">
<DoubleAnimation Duration="0:0:1" To="-180.221" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="button" d:IsOptimized="True"/>
</Storyboard>
Regards,
Roman.

Related

Drag over effect using XAML

I am trying to change Background of a Border when user is dragging a file on it.
I want to define the effect using XAML only.
I tried the below but the Background is not changed when dragging a file on the Border.
<Border Name="dropBorder" BorderThickness="1" AllowDrop="True">
<Border.Triggers>
<EventTrigger RoutedEvent="DragOver">
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Background">
<ColorAnimation From="Transparent" To="#FF444444" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<TextBlock Text="Drag and drop file(s) here" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10"/>
</Border>
I also tried to use DragEnter as below with no results
<EventTrigger RoutedEvent="Border.DragEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="dropBorder"
Storyboard.TargetProperty="Background"
Duration="0:0:0.5"
From="Transparent" To="#FF444444"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
I didnt quite meet your 100% requirement. I created an attached property, which I set via code-behind, so you will want to assess this. Also, moved the color animation around as you were trying to animate a brush, not a color.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="SharedBackgroundBrush" Color="Transparent" />
</Window.Resources>
<Border Name="dropBorder" BorderThickness="1" AllowDrop="True" DragEnter="DropBorder_OnDragEnter" DragLeave="DropBorder_OnPreviewDragLeave" Background="{StaticResource SharedBackgroundBrush}">
<Border.Style>
<Style>
<Style.Triggers>
<Trigger Property="wpfApplication1:DragDropHelper.IsDragOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard Storyboard.Target="{StaticResource SharedBackgroundBrush}" Storyboard.TargetProperty="Color">
<ColorAnimation From="Transparent" To="Yellow" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard Storyboard.Target="{StaticResource SharedBackgroundBrush}" Storyboard.TargetProperty="Color">
<ColorAnimation From="Yellow" To="Transparent" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="Drag and drop file(s) here" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10"/>
</Border>
</Window>
Code:
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void DropBorder_OnDragEnter(object sender, DragEventArgs e)
{
DragDropHelper.SetIsDragOver((DependencyObject)sender, true);
}
private void DropBorder_OnPreviewDragLeave(object sender, DragEventArgs e)
{
DragDropHelper.SetIsDragOver((DependencyObject)sender, false);
}
}
public class DragDropHelper
{
public static readonly DependencyProperty IsDragOverProperty = DependencyProperty.RegisterAttached(
"IsDragOver", typeof (bool), typeof (DragDropHelper), new PropertyMetadata(default(bool)));
public static void SetIsDragOver(DependencyObject element, bool value)
{
element.SetValue(IsDragOverProperty, value);
}
public static bool GetIsDragOver(DependencyObject element)
{
return (bool) element.GetValue(IsDragOverProperty);
}
}
}

WPF Storyboard - same trigger, but reverse behavior

I have a Stackpanel One, which has some content, an Image, and a defualt hidden SubStackpanel. When clickin the Image, the image should rotate 90 degrees, and slide down the SubStackpanel.
When clicking the Image again, the Image should rotate back to its original position, and the SubStackpanel should slide up to the default hidden position.
I almost got this working, the problem is that I dont know how to use the same Trigger event, on two different Storyboard animations. So right now only the first animation on the button and the SubStackpanel occurs, everytime the Image is clicked.
I´ve tried the AutoReverse property, but it fires immediately after the animation is done. This should of course only be happening when the user clicks the Image the second time.
I would like to achieve this, only using markup.
This is my currently code:
<Grid>
<StackPanel Grid.Row="0" Orientation="Vertical" Background="Beige" >
<StackPanel.Triggers>
<EventTrigger SourceName="ImageShowPanelTwo" RoutedEvent="Image.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SubPanel" Storyboard.TargetProperty="(StackPanel.Height)" From="0" To="66" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger SourceName="ImageShowPanelTwo" RoutedEvent="Image.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SubPanel" Storyboard.TargetProperty="(StackPanel.Height)" From="66" To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<TextBlock>Panel One</TextBlock>
<Image Name="ImageShowPanelTwo" Width="26" Height="26" Source="ImageRotate.png" RenderTransformOrigin=".5,.5" >
<Image.RenderTransform>
<RotateTransform x:Name="AnimatedRotateTransform" Angle="0" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
By="0"
To="90"
Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Image.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
By="90"
To="0"
Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
<StackPanel Name="SubPanel" Background="LightGreen" Height="66">
<TextBlock>SubPanel</TextBlock>
<TextBlock>SubPanel</TextBlock>
</StackPanel>
</StackPanel>
</Grid>
Hope you can help :)
Blend for Visual Studio can be used to do it in a better, simpler way, without dealing with a lot of code.
Here is the list of steps of doing it.
Step 1: Open the project in Blend for Visual Studio and in Visual Studio simultaneously.
Step 2: In Blend, create a new storyboard around all the elements that you wish to animate with it.
Let's call it "Storyboard1".
Step 3: Now, open the storyboard that we just created and click on the small arrow just beside the "+" and click on "Duplicate" in the drop down menu. Then, a new storyboard called "Storyboard1_Copy" will be created.
Step 4: Rename this new storyboard to something that you like, say, "Storyboard1_Rev".
Step 5: You must have guessed it by now. Select the duplicated storyboard and from the drop down menu, click on "Reverse".
Step 6: Now you have two storyboards ready: one for animating some elements as you like and the other for reversing that sequence of animation. Just like you call a storyboard from the C# code, you can call the reversing storyboard from the same, subject to some conditions which check if the elements are already animated or not. For this, I use a bool variable, whose value is changed each time some animation occurs on a set of elements(that is, false if the elements are not already animated and true if they are).
Illustration with an example:
I'll create an application with a simple layout. It has a button, an image and a rectangular area on the screen.
The idea is that, whenever you click on the button once, the image should be maximized and should be minimized back to original size when the button is clicked twice and so on. That is, the reverse animation should happen every other time the button is clicked. Here are some screenshots showing how it happens:
You can see that the current state of the image is shown in the button. It shows "Zoom In" when the image is in its initial small size and "Zoom Out" when it is maximized.
And finally, here is the C# code for handling the clicks of the button:
bool flag = false;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (!flag)
{
Button.Content = "Zoom Out";
Storyboard1.Begin();
flag = true;
}
else
{
Button.Content = "Zoom In";
Storyboard1_Rev.Begin();
flag = false;
}
}
All you have to do is have a status flag that shows the current status of the element(s) that you wish to animate and animate it(them) in forward or reverse timeline as per the value of the flag.
Instead of trying to set the Animation using that event, use a bool property to bind to instead:
<Image Name="ImageShowPanelTwo" Width="26" Height="26" Source="ImageRotate.png"
RenderTransformOrigin=".5,.5" >
<Image.RenderTransform>
<RotateTransform x:Name="AnimatedRotateTransform" Angle="0" />
</Image.RenderTransform>
<Image.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsRotated}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
By="0"
To="90"
Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsRotated}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
By="90"
To="0"
Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
I trust that you can define your own bool property and invert it upon each MouseDown event occurrence to complete this functionality. As it is set to true the first Animation will start and as it is set to false the second will start.
This is how I solved my problem:
Created four storyboards for the Stackpanel and the arrow:
<Storyboard x:Key="RotateIconUp">
<DoubleAnimation Storyboard.TargetName="IconExpand" Storyboard.TargetProperty="(TextBlock.RenderTransform).(RotateTransform.Angle)" From="0" To="90" Duration="0:0:0.4" />
</Storyboard>
<Storyboard x:Key="RotateIconDown">
<DoubleAnimation Storyboard.TargetName="IconExpand" Storyboard.TargetProperty="(TextBlock.RenderTransform).(RotateTransform.Angle)" From="90" To="0" Duration="0:0:0.4" />
</Storyboard>
<Storyboard x:Key="SlideGridDown">
<DoubleAnimation Storyboard.TargetName="GridDetails" Storyboard.TargetProperty="(Grid.Height)" From="0" To="180" Duration="0:0:0.4" />
</Storyboard>
<Storyboard x:Key="SlideGridUp">
<DoubleAnimation Storyboard.TargetName="GridDetails" Storyboard.TargetProperty="(Grid.Height)" From="180" To="0" Duration="0:0:0.4" />
</Storyboard>
Then I trigger the storyboards from codebehind when the arrow is clicked:
private void ExpandDetails() {
try {
if (!pm_IsExanded) {
Storyboard Storyboard = (Storyboard)FindResource("RotateIconUp");
Storyboard.Begin(this);
Storyboard = (Storyboard)FindResource("SlideGridDown");
Storyboard.Begin(this);
pm_IsExanded = true;
BorderMain.BorderBrush = pm_BrushConverter.ConvertFromString("#000000") as Brush;
} else {
Storyboard Storyboard = (Storyboard)FindResource("RotateIconDown");
Storyboard.Begin(this);
Storyboard = (Storyboard)FindResource("SlideGridUp");
Storyboard.Begin(this);
pm_IsExanded = false;
BorderMain.BorderBrush = pm_BrushConverter.ConvertFromString("#d0d0d0") as Brush;
}
} catch (Exception ee) {
GlobalResource.WriteToLog("Error in ExpandDetails", ee);
}
}

Event Trigger not working on Storyboard

I was just hoping you could help fill a gap in my WPF knowledge.
(please forgive the generic naming, not sure if it helps)
I've a custom object, MyObject, that implements INotifyPropertyChanged. It has a property called MyCustomProperty, as follows;
public int MyCustomProperty
{
get { return this._myCustomProperty; }
set
{
if (this._myCustomProperty == value)
return;
this._myCustomProperty= value;
OnPropertyChanged("MyCustomProperty");
}
}
This all works.
In my WPF app I have these 3 functions;
private void DoStuff()
{
AddItemsToCanvas();
ChangeValues();
}
private void AddItemsToCanvas()
{
DataTemplate dt = (DataTemplate)FindResource("myDataTemplate");
foreach (MyObject temp in ListOfMyObjects)
{
ContentControl cc = new ContentControl();
cc.ContentTemplate = dt;
cc.Content = temp;
myCanvas.Children.Add(cc);
}
}
private void ChangeValues()
{
// this simply changes the MyCustomPropery in each of the objects
}
The DataTemplate looks like this;
<DataTemplate x:Key="myDataTemplate">
<Canvas>
<TextBlock Name="tb_debug" Text="{Binding Path=MyCustomProperty, NotifyOnTargetUpdated=True}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tb_debug" Storyboard.TargetProperty="(Canvas.Top)" From="0" To="350" Duration="0:0:1.6" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Canvas>
</DataTemplate>
It simply moves the textbox from the top to the bottom of the canvas when MyCustomProperty changes.
When the UserControl is Loaded, I call both AddItemsToCanvas() and ChangeValues(). The Text value updates and displays the correct value, but the Trigger doesn't fire (ie the TextBox doesn't move).
Any time after that, when I call ChangeValues() the Text updates AND the TextBox moves.
Why would the EventTrigger be failing that initial time?
thanks in advance
I'm not sure why the Binding.TargetUpdated does not get called, but it could be only called when a the target is updated once already set and not when its first set (maybe).
But you could just add another EventTrigger on TextBlock Loaded in your DataTremplate to make sure it fires on UserControl Load.
Example:
<DataTemplate x:Key="myDataTemplate">
<DataTemplate.Resources>
<Storyboard x:Key="animation" >
<DoubleAnimation Storyboard.TargetName="tb_debug" Storyboard.TargetProperty="(Canvas.Top)" From="0" To="350" Duration="0:0:1.6" />
</Storyboard>
</DataTemplate.Resources>
<Canvas>
<TextBlock Name="tb_debug" Text="{Binding Path=MyCustomProperty, NotifyOnTargetUpdated=True}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard Storyboard="{StaticResource animation}" />
</EventTrigger>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource animation}" />
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Canvas>
</DataTemplate>

Infinitely rotate rectangle in XAML

How to define XAML to rotate a rectangle infinitely?
So far I found a solution with code but no xaml:
http://www.codeproject.com/Articles/23257/Beginner-s-WPF-Animation-Tutorial
which I use like this:
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
var doubleAnimation = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(1)));
var rotateTransform = new RotateTransform();
rect1.RenderTransform = rotateTransform;
rect1.RenderTransformOrigin = new Point(0.5, 0.5);
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
rotateTransform.BeginAnimation(RotateTransform.AngleProperty, doubleAnimation);
}
But how can I achieve this with XAML only?
Something like this
<Rectangle x:Name="rect1" RenderTransformOrigin="0.5, 0.5">
<Rectangle.RenderTransform>
<!-- giving the transform a name tells the framework not to freeze it -->
<RotateTransform x:Name="noFreeze" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)"
To="-360" Duration="0:0:1" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
Of course you can remove Loaded trigger and run this storyboard whenever you want.

Why isn't this animation working?

I am learning WPF animation, and I created a simple demo app with a pretty straightforward animation. I have divided the main grid into three rows; a Buttons Row at the top, and two content rows that for the remainder of the screen, one red and one blue. Complete XAML is below.
There are two buttons, Show Red and Show Blue. When each button is pressed, I want the area below the Buttons Row to change color with a slow top-to-bottom wipe. The Storyboard sets the height of both rows to 0, then animates the desired row to a height of 1*, like this:
<Storyboard>
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
The colors change as expected, but there is no animation. So, my question is simple: Why isn't the animation working?
I am using a custom animation class, GridLengthAnimation (adapted from this CodeProject article) to animate the grid lengths. I have reproduced the class below.
To recreate the demo project: To recreate my demo project, create a new WPF project (I used VS 2010) and replace the XAML in MainWindow.xaml with the following:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Utility="clr-namespace:Utility" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="Buttons" Height="35" />
<RowDefinition Name="RedRow" Height="0.5*" />
<RowDefinition Name="BlueRow" Height="0.5*" />
</Grid.RowDefinitions>
<!-- Buttons -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Show Red" Width="100" Margin="5" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="Show Blue" Width="100" Margin="5" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
<!-- Grid Fills-->
<Border Grid.Row="1" Background="Red" />
<Border Grid.Row="2" Background="Blue" />
</Grid>
</Window>
There is no code-behind added to MainWindow.xaml.
Add a C# class to the project named GridLengthAnimation.cs. Replace the code in that class with the following:
using System;
using System.Windows.Media.Animation;
using System.Windows;
namespace Utility
{
/// <summary>
/// Enables animation of WPF Grid row heights and column widths.
/// </summary>
/// <remarks>Adapted from Graus & Sivakumar, "WPF Tutorial - Part 2 : Writing a custom animation class",
/// http://www.codeproject.com/KB/WPF/GridLengthAnimation.aspx, retrieved 08/12/2010.</remarks>
internal class GridLengthAnimation : AnimationTimeline
{
static GridLengthAnimation()
{
FromProperty = DependencyProperty.Register("From", typeof(GridLength),
typeof(GridLengthAnimation));
ToProperty = DependencyProperty.Register("To", typeof(GridLength),
typeof(GridLengthAnimation));
}
public override Type TargetPropertyType
{
get
{
return typeof(GridLength);
}
}
protected override Freezable CreateInstanceCore()
{
return new GridLengthAnimation();
}
public static readonly DependencyProperty FromProperty;
public GridLength From
{
get
{
return (GridLength)GetValue(FromProperty);
}
set
{
SetValue(FromProperty, value);
}
}
public static readonly DependencyProperty ToProperty;
public GridLength To
{
get
{
return (GridLength)GetValue(ToProperty);
}
set
{
SetValue(ToProperty, value);
}
}
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
{
double fromVal = ((GridLength)GetValue(FromProperty)).Value;
double toVal = ((GridLength)GetValue(ToProperty)).Value;
if (animationClock.CurrentProgress != null)
{
if (fromVal > toVal)
{
return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal, GridUnitType.Star);
}
else
{
return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal, GridUnitType.Star);
}
}
else
{
return null;
}
}
}
}
I found my answer in this blog post. It turns out there is a problem animating height or width properties. I worked around the problem by using a dissolve effect, instead of a wipe. To animate a dissolve, declare both controls in the same Grid row and column, which will load them on top of each other. Declare the default control last, which will make it the visible control. Then, animate the Opacity value of the default control to zero to hide it, and back to 1 to show it.
If the controls being animated are UserControls or other controls you need to click on, you need to take one more step. That's because setting the Opacity of a control to zero simply makes it invisible. It will still prevent a click on the control beneath it. So, declare a Render.Transform on the default control, then animate the ScaleY property to set it to 0 when invisible and 1 when showing.
Here is an example from the production app I am working on. It switches between a note list and a calendar (two different UserControls) in the Navigator pane of an Explorer-style interface. Here is the declaration of the two controls:
<!-- ClientArea: Navigator -->
<Grid x:Name="Navigator">
<View:CalendarNavigator x:Name="Calendar" />
<View:NoteListNavigator x:Name="NoteList">
<View:NoteListNavigator.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1" />
</View:NoteListNavigator.RenderTransform>
</View:NoteListNavigator>
</Grid>
Note the declaration of the ScaleTransform on the note list. I use a couple of Ribbon buttons to switch between the two UserControls:
<ribbon:RibbonToggleButton x:Name="NoteListViewButton" LargeImageSource="..\Images\ListViewLarge.png" SmallImageSource="..\Images\ListViewSmall.png" Label="Note List" Click="OnViewButtonClick">
<ribbon:RibbonToggleButton.Triggers>
<EventTrigger RoutedEvent="ribbon:RibbonToggleButton.Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="(View:NoteListNavigator.RenderTransform).(ScaleTransform.ScaleY)" To="1" Duration="0:0:0" />
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
<ribbon:RibbonToggleButton x:Name="CalendarViewButton" LargeImageSource="..\Images\CalendarViewLarge.png" SmallImageSource="..\Images\CalendarViewSmall.png" Label="Calendar" Click="OnViewButtonClick">
<ribbon:RibbonToggleButton.Triggers>
<EventTrigger RoutedEvent="ribbon:RibbonToggleButton.Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="(View:NoteListNavigator.RenderTransform).(ScaleTransform.ScaleY)" To="0" Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
The ScaleY transforms get the invisible note list out of the way when the Calendar is showing so that I can click on my calendar controls. Note that I needed fully-qualified references to the ScaleY properties in my Storyboards. That's why the references are enclosed in parentheses.
Hope that helps someone else down the road! It's likely to be me, since I'll probably forget how I did this...

Resources