Animating WPF buttons from inside a style - c#

I'm currently trying to make buttons on my forms animate using WPF - this is part of a University course, so it's all about demonstrating knowledge rather than looking good.
We've been shown how to animate per-button, but since I want the animation to be the same on every button I'm using a style - something we've not been taught and which finding documentation for is like finding evidence of Big Foot, IMO.
My code so far is this:
<Style TargetType="{x:Type Button}" x:Key="ButtonAnimation">
<Style.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Angle"
To="360" Duration="0:0:1"
FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="ScaleY"
To="0.1" Duration="0:0:0.5"
FillBehavior="Stop" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="ScaleX"
To="0.1" Duration="0:0:0.5"
FillBehavior="Stop" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
The TargetProperty="" values are incorrect, and for the life of me I cannot find anywhere online that demonstrates what should be there. The values currently there are what you would have if the animation was applied to each button rather than in a style.
How do I get this to work? What is the correct TargetProperty?

I think this...
Storyboard.TargetProperty="Angle"
...should be...
Storyboard.TargetProperty="(Button.RenderTransform).(RotateTransform.Angle)"
And the other ones:
Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleY)"
Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleX)"
You might have to replace RenderTransform with LayoutTransform, depending on what you are using in your markup.
However, this will only work if you only have one of the two Transforms, i.e. RotateTransform or ScaleTransform. If you have them in a TransformGroup, things get even more complicated. If you have defined the RotateTransform as the first child and the ScaleTransform as the second child of the TransformGroup, then this should work (not tested):
(Button.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)
(Button.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.X)
(Button.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.Y)
However, you have to be careful when changing the order of the Transforms or remove one of them because this will break your animations...
Good luck!

Related

WPF EventTriggers.EnterActions not working

WPF EventTriggers.EnterActions not working.
Ust two similar Storyboard in Trigger.Actions - work, but in Trigger.EnterActions - no.
Trigger on checkbox 2 works, but trigger on checkbox 1 not firing.
WPF EventTriggers.EnterActions not working.
Ust two similar Storyboard in Trigger.Actions - work, but in Trigger.EnterActions - no.
<StackPanel>
<CheckBox Content="checkbox 1">
<CheckBox.Triggers>
<EventTrigger RoutedEvent="CheckBox.Checked">
<EventTrigger.EnterActions>
<BeginStoryboard Name="enter">
<Storyboard>
<DoubleAnimation From="1"
To="0.5"
Duration="0:0:1"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.EnterActions>
<EventTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="enter"/>
</EventTrigger.ExitActions>
</EventTrigger>
</CheckBox.Triggers>
</CheckBox>
<CheckBox Content="checkbox 2">
<CheckBox.Triggers>
<EventTrigger RoutedEvent="CheckBox.Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard >
<DoubleAnimation From="1"
To="0.5"
Duration="0:0:1"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</CheckBox.Triggers>
</CheckBox>
</StackPanel>
WPF EventTriggers.EnterActions not working. Ust two similar Storyboard in Trigger.Actions - work, but in Trigger.EnterActions - no.
See documentation TriggerBase.EnterActions:
Gets a collection of TriggerAction objects to apply when the trigger object becomes active. This property does not apply to the EventTrigger class.

WPF animation won't repeat

There is an MSDN article that specifically addresses my question but it does not provide a solution in my case:
http://msdn.microsoft.com/en-us/library/aa970493(v=vs.110).aspx
I want to trigger an animation from my view model where I fade out one textblock and subsequently fade in another textblock to appear like the text is updating. I need to run this animation 3 times in a row. It would be nice if I could just set the Animate property to "On" 3 times in a row and it would start the animation repeatedly but this does not seem to work.
The next best option would be to notify my ViewModel when the animation is complete allowing me to set the Animate property to "Off" and then change it back to "On". This doesnt seem to work for 2 reasons. If "Off" and "On" are set in the same method call the View doesnt seem to update the property until after that threads method execution is complete. And the second problem is that the Completed event of the animation is NEVER CALLED...this is something that is a constant issue for me.
Any other suggestions?
Here is a sample of the code:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Animate}" Value="On">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Show one textblock and hide the other -->
<DoubleAnimation
Storyboard.TargetName="txtMessage"
Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0" />
<DoubleAnimation
Storyboard.TargetName="txtMessage2"
Storyboard.TargetProperty="Opacity"
To="0.0" Duration="0:0:0" />
<!-- Now animate the text blocks back to their original setting -->
<DoubleAnimation
Storyboard.TargetName="txtMessage"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:1"
Completed="Animation_Completed" />
<DoubleAnimation
Storyboard.TargetName="txtMessage2"
Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:1"
BeginTime="0:0:1"
Completed="Animation_Completed" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</DataTemplate.Triggers>
Again, for some reason the Animation_Completed handler is never hit...
Updated 5/8 to spell out more specifically what I need this animation to do.

How to specify property path?

In a given xaml, there is a part:
<ColorAnimation Storyboard.TargetProperty="Background.(SolidColorBrush.Color)" To="Black" Duration="0:0:3"/>
If I specify only "Background", then it doesn't work. Why? Where can I get some info about making path?
P.S.: I seen some really crazy path, like "(blablabla).(blablabla).(blablabla.blablabla)" and it makes me nervous, because I can't find what does it means easily...
It doesn't work if you only specify Background because the storyboard animates a color, and this color is the Color property of the Background property.
Background is a brush, so it could contain any type of brush. In order to access the Color the brush needs to be converted to a SolidColorBrush.
This is basically what the expression Background.(SolidColorBrush.Color) does. It converts Background to a SolidColorBrush and then accesses the Color property from it.
In c# code you would write
((SolidColorBrush)Background).Color = someColor;
Try this
Example1
<Button>
<Button.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" From="Transparent" To="Red" Duration="0:0:0.1"></ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
example2
<Grid>
<Button x:Name="Button"></Button>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="Button" Storyboard.TargetProperty="Background.Color" From="Transparent" To="Green" Duration="0:0:0.1"></ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>

TriggerAction object must be associated with one and only one trigger object

I defined BeginStoryboard object in resources in style.
In same style definition I would like use this BeginStoryboard object in 2 event triggers which fire animation.
It is possible that 2 different event triggers will use same object or I must define 2 different BeginStoryboard objects?
<Style x:Key="SerialPoster" TargetType="Border">
<Style.Resources>
<BeginStoryboard x:Key="SerialPosterBeginStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX"
From="0"
To="1"
Duration="0:0:2"
AccelerationRatio="1" />
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY"
From="0"
To="1"
Duration="0:0:2"
AccelerationRatio="1" />
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[1].Angle"
From="70"
To="0"
Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</Style.Resources>
<!-- TriggerAction object must be associated with one and only one trigger object. -->
<Style.Triggers>
<EventTrigger RoutedEvent="Border.Loaded">
<EventTrigger.Actions>
<StaticResource ResourceKey="SerialPosterBeginStoryBoard"/>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Border.MouseEnter">
<EventTrigger.Actions>
<StaticResource ResourceKey="SerialPosterBeginStoryBoard" />
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
Yes, it is possible to reference resource and so to reuse the resource instance at different places.
No need to place the Storyboard inside Style.Resource just place it inside Window.

WPF Animation Relative Values

I am trying to learn about Storyboards and animation in WPF. I have a very simple question which I cannot seem to answer.
I have a rectangle that is placed in a grid. I am trying to move the rectangle from one side of the grid to the other side. I can get the rectangle to move successfully. However the issue is that I'm specifying the From & To values. So in my code below I have hard coded the from (50) & to (300) values. What I want to happen is for the rectangle to go to the other side of the grid without me hard coding a value. Because if a user resizes the window the values I put in will be a waste of time.
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
From="50"
To="300"
Duration="0:0:5"/>
<ColorAnimation
Storyboard.TargetProperty="Fill.Color"
To="Yellow"
BeginTime="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Rectangle.Triggers>
Instead of using the Width property, you could use ScaleTransform of RenderTransform. This will let you scale according to the parent element and not the width itself. See the example underneath and see if it works...
<Rectangle Height="70" Fill="Green">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0.2"/>
</TransformGroup>
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
From="0.2"
To="1"
Duration="0:0:5"/>
<ColorAnimation
Storyboard.TargetProperty="Fill.Color"
To="Yellow"
BeginTime="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
When animating sizes/locations with Storyboards, always use the ActualWidth and/or Actualheight properties instead of the usual Width and Height properties because the Width and Height properties can often have NaN (Not a number) values which will blow up when the Storyboard tries to access it/them.
For this animation, you'll need to declare the Name property of the parent element because we'll reference it in the Storyboard. Try this and let me know how you get on:
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50"
To="{Binding ActualWidth, ElementName=ParentContainer}" Duration="0:0:5"/>
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="Yellow"
BeginTime="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Rectangle.Triggers>

Categories