I'm making a Windows Phone 8 app and would like to first fade out and then -- and only then -- actually turn a UIElement's Visibility to "Collapsed". However, I can't figure out how to make them happen asynchronously.
I'm using Storyboards and Blend. Here are my storyboards to toggle my little "Popup" StackPanel:
<VisualStateGroup x:Name="PopupStates">
<VisualState x:Name="PopupDisplayed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Popup">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Popup" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="PopupHidden">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Popup">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Popup" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
How can I time my storyboard events to happen one-after-another?
You can set the BeginTime property of the second animation to be the duration of the first.
From the MSDN page:
The BeginTime property is useful for creating timelines that play in a sequence: by increasing the BeginTime of successive timelines that share the same parent Storyboard, you can stagger their play times.
The example on that page shows how you use it:
<!-- Animates the rectangle's width. No
BeginTime specified so by default begins
as soon as it's parent (the Storyboard)
begins. -->
<DoubleAnimation
Storyboard.TargetName="MyAnimatedRectangle"
Storyboard.TargetProperty="Width"
To="300" Duration="0:0:1" />
<!-- Animates the rectangle's opacity. A BeginTime
of 3 seconds specified so begins three seconds
after the Storyboard begins (total of 5 seconds)-->
<DoubleAnimation BeginTime="0:0:3"
Storyboard.TargetName="MyAnimatedRectangle"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:1" />
The first animation starts as soon as the Storyboard starts and lasts 1 second. The second animations starts 3 seconds after the Storyboard starts and also lasts 1 second.
So in your example you'd set the duration of the animation that fades the popup to 2 seconds (say):
<DoubleAnimation Duration="0:0:2" To="0"
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="Popup" d:IsOptimized="True"/>
and then set the begin time of the animation that sets the visibility to 2 seconds:
<ObjectAnimationUsingKeyFrames BeginTime="0:0:2"
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="Popup">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
I have a grid containing two elements, a search form and a custom UC to show items. When my page loads, the viewer control is hidden. I ant to animate it in with a storyboard when a button on the search form is clicked. I'm stuck at the To=... part of the animation. I want to animate it to the height of the parent container and have it slid up from the bottom. Is this possible?
For reference, this is my tree structure:
Page resources:
<Storyboard x:Name="animateViewSlide" Storyboard.TargetName="SearchForm" >
<DoubleAnimation Storyboard.TargetProperty="Height" To="???" Duration="100"/>
</Storyboard>
Control body:
<Grid x:Name="LayoutRoot">
<Grid x:Name="SearchForm" Margin="6,6,6,70">
<uc:AwesomeViewer x:Name="awesomeView" Awesomeness="{Binding SelectedAwesomeThing}" Visibility="Collapsed"/>
</Grid>
UPDATE
I did figure out a bit better way of doing this, instead of using the Margin to step through the animation, just animate the Y axis with a TranslateTransform like;
<Grid x:Name="ItemToMove"
Visibility="{Binding Condition, Converter={StaticResource boolVisConv}}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="30,0,0,-32" Opacity="0">
<Grid.RenderTransform>
<TranslateTransform x:Name="notified" Y="40" />
</Grid.RenderTransform>
<Grid.Triggers>
<EventTrigger RoutedEvent="Grid.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="notified" Storyboard.TargetProperty="Y">
<SplineDoubleKeyFrame KeyTime="0:0:1.25" Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ItemToMove" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="0:0:1.55" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
END UPDATE
Your trouble is going to stem from trying to animate an automatically calculated value (Height) so I would scrap that idea unless you want to use hard coded size values on your Parents/Children or just assume for example that if the app is restricted to Portrait Mode only....then your "To" value is 800px, except I imagine that's not what you're looking for.
Now then, you would usually hope you could just feed the rendered size into your "To" value, like maybe via To="{Binding ActualHeight, ElementName=LayoutRootOROTHERParentContainer}" right? Yet nay, that won't work either...
So while someone else may have a better way, the only way I've found is to instead leave the Auto calculated sizes alone to do their thing, and instead animate a stepping of the object Margin via ObjectAnimationUsingKeyFrames which when paired with an Opacity transition via DoubleAnimationUsingKeyFrames actually looks quite nice, except it will add a bunch of additional XAML for the process. So as a concept you might try something more like this (however if you come across a better solution, I'd sure like to know it also. As a designer for a living, I run into this relatively often.)
Concept Example;
(The Storyboard)
<Storyboard x:Name="Kaboom">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="border">
<DiscreteObjectKeyFrame KeyTime="0:0:0.1">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,750,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.2">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,600,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.3">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,550,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.4">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,500,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.5">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,450,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.6">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,400,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.7">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,350,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.8">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,300,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.9">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,250,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,200,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.1">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,150,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.2">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,100,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.3">
<DiscreteObjectKeyFrame.Value>
<Thickness>0,50,0,0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.4">
<DiscreteObjectKeyFrame.Value>
<Thickness>0</Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border">
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="0.01"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.6" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
A quick adhoc example of the concept in action;
<Grid Height="800" Width="480" x:Name="ParentContainer">
<!-- Your SearchForm thingy with its funky margins already set like your example -->
<Rectangle Margin="6,6,6,70" Fill="Green" />
<Button Height="100" Width="100" Content="Click Me!" HorizontalAlignment="Left" VerticalAlignment="Top">
<!-- Quick button to show it in action -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ControlStoryboardAction Storyboard="{StaticResource Kaboom}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<!-- Your Awesomeness -->
<Border x:Name="border" Width="300" Background="Blue" HorizontalAlignment="Right" Margin="0,800,0,0" Opacity="0">
<TextBlock x:Name="textBlock" Text="OH
SNAP!!!
Say
WHAAA??"
TextAlignment="Center" VerticalAlignment="Center"
FontSize="40" FontWeight="Bold" Foreground="White"/>
</Border>
</Grid>
Anyway, probably not exactly what you're hoping for, but is an effective alternative especially with a little tweaking unless like I said, you want to hardcode your object sizes so you have something viable to set your "To" value to...which, in the case of a phone that's restricted to the standard 800x480 Portrait Mode Only you may very well want to. However if you don't have that option, I've pulled off some pretty nifty designs using this method instead.
Hope this helps and best of luck!
How would i go about setting a property that doesn't seem to have an animation type associated with it? Specifically, I'd like to change a control's VerticalAlignment whenever an EventTrigger is activated. Here's my current status/failed attempt:
<EventTrigger RoutedEvent="my:MenuHelper.MenuIsReversed">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="VerticalAlignment" Storyboard.TargetName="Bouncy_Bar">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Top"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Which yields this exception:
Cannot animate the 'VerticalAlignment' property on a
'System.Windows.Controls.Border' using a
'System.Windows.Media.Animation.ObjectAnimationUsingKeyFrames'. For
details see the inner exception.
Inner exception:
The animation(s) applied to the 'VerticalAlignment' property calculate
a current value of 'Top', which is not a valid value for the property.
I'm not sure if i'm improperly qualifying the VerticalAlignment type or if this is simply the wrong way to go about setting an atypical animation property.
Sorry, for the quick answer of my own question. I found that I wasn't laying out the XAML to specify the target type well enough. Here was the working result:
<EventTrigger RoutedEvent="pill:PillMenuHelper.MenuIsReversed">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Bouncy_Bar" Storyboard.TargetProperty="RenderTransform.Children[1].ScaleY" To="-1" Duration="0:0:.002"/>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="VerticalAlignment" Storyboard.TargetName="Bouncy_Bar">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<VerticalAlignment>Top</VerticalAlignment>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
i want to be able to change the tooltip on Checked and Unchecked, i tried:
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="ToolTipService.ToolTip" Storyboard.TargetName="btn">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:String>Button is checked</System:String>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
but it doesnt seem to work, what am i doing wrong?
Once before I also, ran into the same problem. So, I left the VSM away. I made the ToolTip as Resource and applied it for the control. Using the internal state change properties I updated the tooltip value using a converter.
HTH.
When button1 is clicked I want this sequence
RectangeA becomes visible
RectangeA opacity changed from 0 to 75% over lets say 3 seconds
ControlB becomes visible.
Steps 1 and 3 are easy with imperative code, but I'm assuming I need to learn how to use story boards to do step 2.
Here is the storyboard that describes your sequence:
<Storyboard x:Key="animate">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button1" Storyboard.TargetProperty="(UIElement.Opacity)">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<LinearDoubleKeyFrame KeyTime="00:00:03" Value="0.75"/>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button1" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="Visibility.Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="control" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="Visibility.Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
You can trigger it in xaml through EventTrigger or in code through TryFindResource().
Here is the link on Animation Overview MSDN Article and you can find there answers on your questions on many WPF animations topics.