The ellipse can't rotate in datatrigger - wpf

I want to rotate the ellipse when ProgressBar is set to true in DataTrigger. But It isn't work.
I test the ellipse is work OK with Opacity. But it isn't work with RotateTransform. And the ellipse is also work with EventTrigger.
Which is my mistake and how I fix it.
Thanks for reading my issue.
<Ellipse Name="ProgressEllipse" Width="40" Height="40" StrokeThickness="7" Stroke="{StaticResource ProcessEllipseGradient}" Margin="5,0,0,0">
<Ellipse.RenderTransform>
<RotateTransform CenterX="20" CenterY="20" Angle="0"/>
</Ellipse.RenderTransform>
<!--<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded" SourceName="ProgressEllipse">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ProgressEllipse"
Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:1"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>-->
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=ProgressBar}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
From="0"
To="1"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
Addition information
Sorry WPF-it. I make my question not clear.
The Ellipse is work OK when It located in form directly. But It not work when located in a Template.
Follow is my code.
https://www.mediafire.com/?7rb8gvthx94rfiy
If I omit "RelativeSource={RelativeSource self}" in the Trigger. It will throw exception(Cannot animate '(0).(1)' on an immutable object instance)
In the sample code. I have three ellipse. The left ellipse is not work when I locate it in the template(I try to make it work, but It don't work). The center ellipse is the same code with the ellipse in template. But It work OK when I locate it directly in the form. And I test ellipse with Opacity property in the template. And It work OK. I don't know which is my mistake.
Can you give me a hand.
Thanks for reading my question

Ellipse.ProgressBar property does not exist in WPF. Your are probably referring some property from the underlying DataContext of Ellipse.
So change Binding="{Binding RelativeSource={RelativeSource Self}, Path=ProgressBar}" to simply Binding="{Binding Path=ProgressBar}"
Also make sure that this ProgressBar property generates property change notifications so that trigger will execute the action ...
Plus you should also have an EndStoryBoard also.
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ProgressBar}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="MyStoryboard">
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
From="0"
To="1"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="MyStoryboard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>

Related

Stop StoryBoard after currently running animation is completed

I want to stop WPF StoryBoard after property of control IsAnimating is changed to false, but I need to stop animation not instantly, but first complete currently running animation cycle and then stop it (I'm using this XAML now, but it stops my animation instantly):
<UserControl x:Class="App.Controls.ProgressCircle"
x:Name="me"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" >
<Ellipse Name="Circle" Width="30" Height="30" Fill="Green" >
<Ellipse.Style>
<Style>
<Style.Resources>
<Storyboard x:Key="Pulsing">
<DoubleAnimation From="30.0" To="0.0" Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
Storyboard.TargetProperty="Width" />
<DoubleAnimation From="30.0" To="0.0" Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
Storyboard.TargetProperty="Height" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding IsAnimating, ElementName=me}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="Pulsing" Storyboard="{StaticResource Pulsing}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="Pulsing" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</UserControl>
A "quick and dirty" solution would be to start a new animation in the DataTrigger.ExitActions that animates the height and width properties to the values they had before the whole animation was started. This could look like this:
<Style.Triggers>
<DataTrigger Binding="{Binding IsAnimating, ElementName=me}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="Pulsing" Storyboard="{StaticResource Pulsing}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1"
Storyboard.TargetProperty="Height" />
<DoubleAnimation Duration="0:0:1"
Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
The important thing is that if you do not set the the To value on an animation, then the target value will be the one that the dependency property previously had (the local value of 30.0 in this case). You do not have to stop the 'Pulsing' storyboard in this case because this is automatically done when you start a new storyboard that animates the same dependency properties.
Obviously, this is not the optimal solution as you do not integrate the current status of the Pulsing animation (i.e. at which point in time it was when IsAnimating is set to false). As far as I know, there is no built-in functionality in WPF to achieve this kind of functionality, but it might be possible to implement a custom ConstrallableStoryboardAction that respects all these information and that can be set in the DataTrigger.ExitActions instead. It might also be worth your while to check some of Animation How-To topics in the MSDN library.

How to reference a DoubleAnimation from a control's resources in a window resources' StoryBoard?

I'm trying to make a "Please wait" animation. It could consist of 4 Rectangles having the exact same animation, but slightly delayed.
Here's one of them:
<Rectangle Fill="Blue" Margin="4">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard x:Name="FlipIt">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="0:0:1" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlipIt"/>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
(I used a trigger on Visibility so it doesn't eat up CPU uselessly)
So basically, what I could do is to simply copy paste these 24 lines of XAML Style for each of my Rectangles. But what if I decided that 9 rectangles look better? That's gonna get nuts pretty fast.
My idea was to have, in my Window's Resources, this style, and in each Rectangle's Resources, a DoubleAnimation for whatever it wants to do (in the case of 9 Rectangles, I may want the middle one to spin instead of scale on its Y axis).
The question is then: how can I reference a Rectangle's DoubleAnimation (given the fact that they'd all have the same x:Key) inside my Style's Storyboard (contained in my Window's Resources)?
Edit 1:
Here's how I think it should go:
Window's resources:
A style named "AnimIt"...
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard x:Name="FlipIt">
<Storyboard Children="?????????????"/>
</BeginStoryboard>
</Trigger.EnterActions>
...
A Rectangle:
<Rectangle Style="{StaticResource AnimIt}">
<Rectangle.Resources>
<DoubleAnimation x:Key="anim" Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="0:0:1" RepeatBehavior="Forever"/>
</Rectangle.Resources>
I dont really know what to put in place of the "????????". I tried playing with RelativeSource FindAncestor, but have no idea how to reference the DoubleAnimation there.

WPF XAML Grid Visibility Trigger

I have a status message located on the first row of my grid and I want it to slide in and out when the visibility changes.
The first visibility trigger works great and slides the first grid row open quickly. As soon as I add the 'Collapsed' trigger, nothing works at all. How do I reverse the animation to slide closed when the visibility is set to collapsed?
<Grid Grid.Row="0" Height="55" Visibility="{Binding StatusMessageVisibility, Mode=TwoWay}">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="55" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Collapsed">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="55" To="0" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="Hi There" />
</Grid>
You should remove the Visibility binding in your grid and use a DataTrigger that binds to the StatusMessageVisibility property. If you bind the grid's visibility then once it's collapsed it's collapsed and you won't be able to see the animation.
Also, instead of having two data triggers with EnterActions, use a single data trigger that also has an ExitAction for the collapsed state:
<Grid Grid.Row="0" Height="55">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusMessageVisibility}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="55" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="55" To="0" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="Hi There" />
</Grid>

How to Bind DoubleAnimation Enabled/Disabled

Please tell me how Can I solve my problem. The situatino is as follow:
I had a list box with image inside it, I would like some of the image to blinking, some not. I had the property "Emergeny" if it is true the image should blink, now the question is how can I bind it. I try to Bind to "Duartion" or "To" propperty but I receives an error. The code below is my image which is blinking.
<Image Height="32" Width="32" Source="{Binding Emergency, Converter={StaticResource boolToPath}}">
<Image.Style>
<Style>
<Style.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.Opacity)" BeginTime="0:0:0" Duration="0:0:0.5"
From="1.0" To="0.0" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Instead of triggering on Event.Loaded, trigger on your property:
<Trigger Property="IsEmergency" Value="True">
<BeginStoryboard .../>
</Trigger>

How to animate ScaleY when Element becomes visible

I'd like to make a WPF UI Element appear to expand vertically when its Visibility property transitions to "Visible". I don't want to hard-code the Height in the animation since I'd like to apply this animation to any UI Element as a Style. So, I'm trying to use ScaleY but am not having any luck. Here is the XAML for the style and the listbox:
<Style x:Key="VerticalGrow" TargetType="ListBox">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="TransformGroup.ScaleTransform.ScaleY" BeginTime="0:0:0.5" From="0" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<ListBox Grid.Row="2" MaxHeight="60" MinHeight="60" Visibility="{Binding MyViewModel.ListBoxVisibility}" IsSynchronizedWithCurrentItem="False" ItemsSource="{Binding MyViewModel.ListBoxItems}" Style="{DynamicResource VerticalGrow}" IsTabStop="True">
</ListBox>
I get an runtime exception complaining that:
"Cannot convert the value in attribute 'Style' to object of type 'System.Windows.Style'. Cannot resolve all property references in the property path 'TransformGroup.RenderTransform.ScaleTransform.ScaleY'. Verify that applicable objects support the properties. Error at object 'System.Windows.Controls.ListBox' in markup file 'MyApp;component/mainwindow.xaml' Line 69 Position 399."}
ListBox doesn't have a property TransformGroup. I think you want to set RenderTransform or LayoutTransform to a ScaleTransform, and then animate that.
<Style x:Key="VerticalGrow" TargetType="ListBox">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.ScaleY"
BeginTime="0:0:0.5" From="0" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>

Resources