Setting multiple visual states in one go? - c#

I have four visual states defined each affecting different child controls within the same silver-light control.
Is it possible for me to create other visual states which invoke a combination of these others?
So if I have Visual_Group_1, Visual_Group_2, Visual_Group_3, Visual_Group_4
Is it possible to make, say a
Visual_Comb_1 group which uses the
states in Visual_Group_1 and
Visual_Group_3?
Then make another one called Visual_Comb_2 which uses Visual_Group_4 and Visual_Group_3?
I'm happy to implement a solution in xaml or codebehind or a combination of both.
The alternative I'm looking at currently involves tonnes of code copy+paste and I'm not too keen to do that.
Some more detail per request:
This is what I roughly have right now:
<VisualState x:Name="State1">
<ColorAnimation Storyboard.TargetName="Path1"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="Blue" Duration="0:0:0.5" />
// fade out the rest of the paths...
<ColorAnimation Storyboard.TargetName="Path2"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetName="Path3"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetName="Path4"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
</VisualState>
<VisualState x:Name="State2">
<ColorAnimation Storyboard.TargetName="Path3"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="Red" Duration="0:0:0.5" />
// fade out the rest of the paths...
<ColorAnimation Storyboard.TargetName="Path2"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetName="Path1"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetName="Path4"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
</VisualState>
<VisualState x:Name="State3">
<ColorAnimation Storyboard.TargetName="Path4"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="Pink" Duration="0:0:0.5" />
// fade out the rest of the paths...
<ColorAnimation Storyboard.TargetName="Path2"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetName="Path1"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetName="Path3"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:0.5" />
</VisualState>
My objective is to have a control which when you click on cycles from state1 to state3, each state fades in a different path while fading out the other paths. My problem is that there is a tonne of copy+paste in the 'fade out the rest of the paths' section, so if I wanted to add a Path5 it would mean adding it to every single visual state already defined, or if I wanted to change the fadeoff colour or animation I would have to do it to every visual state.

Thanks for providing the XAML. This is how I would tackle the problem.
First off, create the VisualStates individually for each Path. (I would recommend using a Style instead to save you re-coding a very similar VisualState into each Path, but I'm not familiar enough with them to know if you can apply different colours to each.)
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Path1States">
<VisualState x:Name="Activate">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Path1"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="Blue"
Duration="0:0:0.5" />
</Storyboard>
</VisualState>
<VisualState x:Name="Deactivate">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Path1"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#00000000"
Duration="0:0:0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="Path2States">
<!-- ... etc ... -->
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Now, create a List in the code-behind that contains each of the related objects, and then right your own GoToState function such that it turns on the in state for one object, and calls the off state for the rest.
List<Path> pathList;
public Page() // constructor
{
InitializeComponent();
pathList = new List<Path>();
pathList.Add(Path1);
// and so forth
}
// Call this function when you want to change the state
private void ActivatePath(Path p)
{
foreach (Path listItem in pathList)
{
// If the item from the list is the one you want to activate...
if (listItem == p)
VisualStateManager.GoToState(listItem, "Activate", true);
// otherwise...
else
VisualStateManager.GoToState(listItem, "Deactivate", true);
}
}
If I were better at XAML and styling I might have a cleaner way of creating the VisualStates. However, my forte is more on the logic and coding side. That being said, it's much cleaner that writing out the same VisualState four or five times! :)
Hope this helps!

Related

What is the proper way to implement VisualStates for multiple VisualStateGroups?

This question is a follow-up to my previous question as well as this related question about how VisualStates work in WPF.
Currently, my understanding is that animating the same property within different VisualStateGroups can cause problems (see the linked questions).
To resolve these problems, it requires loop-holes to be taken advantage of. (Perhaps loop-hole isn't the correct term, but it appears that the solution isn't what the WPF designers intended.)
I'm wondering what is the correct way to animate the same property in multiple VisualStateGroups without causing adverse side-effects. If it is not possible, what is the correct route for achieving the same visual behavior for a control?
I was able to find some related documentation at MSDN:
The control is always in exactly one state per group. For example, a Button can have focus even when the mouse pointer is not over it, so a Button in the Focused state can be in the MouseOver, Pressed, or Normal state.
This leads me to a second question...
How can you provide a visual behavior that should only occur when two specific VisualStates are active?
Take for example a ToggleButton:
If the button is Checked, I would like to display Behavior 1.
If the button is Disabled, I would like to display Behavior 2.
Finally, if the button is Checked and Disabled, I would like to display Behavior 3.
In the above example, how would you go about rendering the third visual behavior?
For the first part of your question, you'll want each state to interact with a different individual object for each instead of hitting the same one with each VisualState, as example;
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DisabledState"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckedState"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- Each state interacts with its own object in your ControlTemplate ideally -->
<Border x:Name="CheckedState" Visibility="Collapsed"
Background="Green"/>
<Border x:Name="DisabledState" Visibility="Collapsed"
Background="White" Opacity=".5"/>
Instead of one object sharing a property change like;
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation d:IsOptimized="True"
Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(SolidColorBrush.Color)"
To="White" />
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation d:IsOptimized="True"
Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(SolidColorBrush.Color)"
To="Green" />
</Storyboard>
</VisualState>
<Border x:Name="Background" Background="Blue"/>
As per your second question, A VisualState is going to act as a bool in that it either is, or it isn't in that state. To share a declaration of a state you'd have to add a little more finesse with a MultiTrigger or a converter somewhere or something.
Hope this helps. Cheers
EDIT ADDITION:
So you also have available to you VisualTransition you could employ like;
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition From="Normal"
GeneratedDuration="0:0:0.2"
To="Checked">
<VisualTransition.GeneratedEasingFunction>
<ExponentialEase EasingMode="EaseIn" Exponent="7" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
<VisualTransition From="Checked"
GeneratedDuration="0:0:0.2"
To="Normal">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<!-- etc, etc, etc -->
So you can play with your different eases, your generated duration times, etc.

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>

WPF Animation starts but shows too late

I am building some WPF controls using .Net 4.0. One of these controls, called LoadingPane, is a custom control derived from ContentControl.
The only job of this LoadingPane control is to show a semi-transparent layer over it's contained content when it's IsLoading property is set to true.
I use some animations to do fade-in, fade-out when the IsLoading value changes.
When the overlay is shown an animation rotates a circle of elipses.
So far, so good. This all works very nicely. But here's my problem: when i set the Loading property to true the animation isn't shown directly. It takes about half a second. In this time the fade-in animation has already run, so the opacity effectively goes from 0 to 1 in one step.
Here's my animation code:
<ControlTemplate.Triggers>
<Trigger Property="IsLoading"
Value="True">
<Trigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="EndAnimateLoadingCanvas" />
<BeginStoryboard Name="AnimateLoadingCanvas">
<Storyboard FillBehavior="Stop">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Opacity"
To="1" />
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:02"
Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
From="360"
To="0"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="AnimateLoadingCanvas" />
<BeginStoryboard Name="EndAnimateLoadingCanvas">
<Storyboard FillBehavior="Stop">
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Opacity"
To="0" />
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
The strange thing is, when i use this control in a test window and i click the Loading checkbox repeatedly (and before the animation has finished) then the fade-in/fade-out animation does work as i expect it to work.
Can anyone help? Thanx in advance!
It's hard to see exactly what the problem is without seeing the rest of the code but my guess is that it has to do with the start values of the animated properties.
I implemented a custom control in WPF with a rectangle inside a viewbox and used the triggers + storyboards from the question to see the effect. Indeed, my first attempt did not fade in nor fade out.
What I did to solve it was by specifying From values in the animations so that they work regardless of what the original value of the DP's were:
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Opacity"
From="0"
To="1" />
Notice From="0" in the above animation. The end storyboard was modified the same way, to go from 1 to 0.
For completeness I set the opacity to 0 on the definition of the viewbox element inside the ControlTemplate as well.
Here is the complete source code for the relevant parts. The control is a standard WPF custom control inheriting from Control. It has a single dependency property called IsLoading (bool) which defaults to false:
public bool IsLoading
{
get { return (bool)GetValue(IsLoadingProperty); }
set { SetValue(IsLoadingProperty, value); }
}
// Using a DependencyProperty as the backing store for IsLoading. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLoadingProperty =
DependencyProperty.Register("IsLoading", typeof(bool), typeof(LoadingControl), new UIPropertyMetadata(false));
ControlTemplate - Defined in generic.xaml in the style for {x:Type local:LoadingControl}
<ControlTemplate TargetType="{x:Type local:LoadingControl}">
<ControlTemplate.Triggers>
<Trigger Property="IsLoading" Value="True">
<Trigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="EndAnimateLoadingCanvas" />
<BeginStoryboard Name="AnimateLoadingCanvas">
<Storyboard FillBehavior="Stop">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Opacity"
From="0"
To="1" />
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:02"
Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
From="360"
To="0"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="AnimateLoadingCanvas" />
<BeginStoryboard Name="EndAnimateLoadingCanvas">
<Storyboard FillBehavior="Stop">
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Opacity"
From="1"
To="0" />
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00.5"
Storyboard.TargetName="MyViewBoxje"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Viewbox x:Name="MyViewBoxje" Opacity="0">
<!-- BG with 0x50 alpha so that it's translucent event at 100% visibility -->
<Grid Width="100" Height="100" Background="#50000000">
<Rectangle Width="70" Height="20" Fill="Green" Stroke="Black" StrokeThickness="2" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="360" x:Name="AnimatedRotateTransform" />
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Viewbox>
</Border>
</ControlTemplate>
I used in my main window like so:
<Grid x:Name="LayoutRoot">
<!-- All other stuff here ... -->
<my:LoadingControl IsLoading="{Binding IsLoading}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
And to test it I had a ViewModel with a property IsLoading that is set as DataContext for the main window. And in the constructor of the ViewModel I set IsLoading to true and then start a timer that toggles the value of the property every 5 seconds:
public MainWindowViewModel()
{
IsLoading = true;
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(5);
t.Tick += (s, e) => IsLoading = !IsLoading;
t.Start();
}
I finally figured it out after reading Isak's answer.
Sorry to say that his answer did not help in my case but he got me going in the right direction.
The reason that the first fade-in did not seem to work was because my containing viewbox had it's visibility set to collapsed the whole time the fade-in animation was performed.
This was caused by the ObjectAnimationUsingKeyFrames:
DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
There was no duration specified and the single key frame specified was animated too late.
Adding
Duration="00:00:00"
solved my problem.
Thanks to you all helping!

Hyperlink in Silverlight AccordionItem HeaderTemplate

I have created a HeaderTemplate for my accordions where I want to display a text block on one side of the header and a hyperlink on the right side. The display is working correctly, but the click event is not called when the user clicks, I'm guessing b/c the header itself is trapping the click for expand/contract.
<layoutToolkit:Accordion>
<layoutToolkit:AccordionItem IsSelected="True">
<layoutToolkit:AccordionItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="20">
<TextBlock Margin="0,0,700,0">Cancel Postcards</TextBlock>
<HyperlinkButton Content="Next Call" Foreground="Blue" Click="NextCancel_Click" />
</StackPanel>
</DataTemplate>
</layoutToolkit:AccordionItem.HeaderTemplate>
..... more code ....
Is there a way to get the hyperlink to respond to events without practically creating a new control?
Update: It looks like the header sets all child controls to disabled when expanded which is why the link doesnt work. It will work when you collapse that accordionitem. So, the question now is, how do i prevent the hyperlink from being disabled?
Hey Charlie, I just happened to answer this same question for Epic720. You have to change the Locked VisualState.
Interactive items in Silverlight Accordion Header
Here is the LockedStates VisualStateGroup of the AccordionItem which you should alter. I can post the whole style if you need, though it's quite verbose.
<VisualStateGroup x:Name="LockedStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Locked">
<Storyboard>
<!--
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IsEnabled" Storyboard.TargetName="ExpanderButton">
<DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
</ObjectAnimationUsingKeyFrames>
-->
</Storyboard>
</VisualState>
<VisualState x:Name="Unlocked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IsEnabled" Storyboard.TargetName="ExpanderButton">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>

Animating WPF buttons from inside a style

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!

Categories