Animation to autosized window on startup in WPF - wpf

I am trying to animate an error message popup to expand itself slightly then contract to its original size all in about a half second interval to give emphasis to it in WPF. The window is designed as a border with another border inside that houses a stack panel with an icon on the left and a messagebox to the right. On initialization the messagebox gets the exception populated and the whole window's size is set to sizetocontent. By the way I am using .NET 3.5. Please help!

Add this to your xaml within the outside Border or to the Window
<Style>
<Setter Property="Border.RenderTransform">
<Setter.Value>
<ScaleTransform CenterX="50" CenterY="50" ScaleX="1" ScaleY="1" />
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Border.Loaded">
<EventTrigger.Actions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation Duration="0:0:0.5"
Storyboard.TargetProperty="RenderTransform.ScaleX"
From="1.0" To="1.1" Duration="0:0:0.5"
AutoReverse="True"/>
<DoubleAnimation Duration="0:0:0.5"
Storyboard.TargetProperty="RenderTransform.ScaleY"
From="1.0" To="1.1" Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>

Related

Stop Animation without resetting to start position wpf

I have the following in a style for a button, which is used for a refresh button - when the user hovers the mouse over the button, the image on it (in this case a circular arrow) spins around:
<Style x:Key="RefreshButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Name="buttonContent">
<Image Source="/View/Images/Retry Green2.png" x:Name="Picture" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform x:Name="AnimatedRotateTransform" Angle="0" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="Spinner">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:1"
RepeatBehavior="Forever"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<StopStoryboard BeginStoryboardName="Spinner"/>
</EventTrigger>
</Image.Triggers>
</Image>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="RenderTransform" TargetName="buttonContent">
<Setter.Value>
<TranslateTransform X="0" Y="2"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When the user moves their mouse away from the button, it stops spinning, which is good, but it also reverts to its original position at zero rotation (the same happens when the user clicks the button). Is there any way, within the style, to change the behaviour so that it stops spinning and remains at its new rotation orientation, both when the user moves the mouse away or when they click the button and it transforms down two points in the second RenderTransform?
The animation should pick up where it left off if the user mouse-overs the button another time.
I know it's a really small thing, but it is a niggle that won't go away and each time I use this template I have another stab at solving the problem without success. I can't help but think I am missing something. I am not helped by the fact that all the related problems I find are to do with animations not stopping at all - narrowing down the searches has proved very difficult.
I believe you are looking for this:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.animation.doubleanimation.isadditive?view=net-5.0
To add to your DoubleAnimation.
EDIT: This coupled with replacing StopStoryBoard with PauseStoryBoard should solve the problem. The final xaml should look like this in the Image.Triggers node:
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="Spinner">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="AnimatedRotateTransform"
Storyboard.TargetProperty="Angle"
IsAdditive="true"
From="0" To="360" Duration="0:0:1"
RepeatBehavior="Forever"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<PauseStoryboard BeginStoryboardName="Spinner"/>
</EventTrigger>
</Image.Triggers>

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.

XAML DataTrigger to enable StoryBoard

I have a xaml page where there is a symbol defined as a path.
The path has a RenderTransform to make it rotate.
The path is defined as:
<Path x:Name="MyPath" Width="80.6014" Height="80.9457" Canvas.Left="526.107" Canvas.Top="812.571" Stretch="Fill" Fill="#FFBABABA" Data="F1 M …. Z ">
<Path.RenderTransform>
<RotateTransform x:Name="Rotating" CenterX="40.62" CenterY="40.79" Angle="0"/>
</Path.RenderTransform>
</Path>
The Path can rotate when triggered by the ancestor Canvas Load event:
<Canvas.Triggers>
<EventTrigger RoutedEvent="ContentControl.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Rotating" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:03.0" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
But I would like it to rotate as a result of a bound property (Active) which implements INotifyPropertyChanged, I guess, using a DataTrigger.
I just don't know how to tie it together.
Can anyone point me in the right direction?
OK, I've got it.
The Path object needs a style:
<Path Style="{StaticResource RotateStyle}" x:Name="Path" ...
And the style can be defined as:
<Style x:Key="RotateStyle" TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Active, Mode=OneWay}" Value="On">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="360" Duration="0:0:03.0" RepeatBehavior="Forever"
Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:03.0"
Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
This article gave me the answer: WPF RotateTransform DataTrigger

Style.Trigger vs. Grid.Trigger

In my Code I have defined two Triggers, a Label and a Canvas.
The description of my problem:
When the cursor goes straight across the Label, the Style.Trigger gets activated and the background colour changes (to orange). When the cursor runs across the canvas-area the Grid.Trigger gets activated and changes the background color(to violet). So far, so good.Is the cursor now, running (after the Grid.Trigger was active) across the label-area the background does not change at all.
It seems to me that the Grid.Trigger gets priority once it was active.
<Window x:Class="Sample01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<!-- Defined Style starts here -->
<Style x:Key="{x:Type Label}" TargetType="{x:Type Label}" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.Setters>
</Trigger.Setters>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="DarkOrange" Duration="0:0:0.5"
Storyboard.TargetProperty="Background.Color"
/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="White" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
<!-- End defined Style-->
</Grid.Resources>
<!-- Define Trigger -->
<Grid.Triggers>
<EventTrigger RoutedEvent="MouseEnter"
SourceName="canvas">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="BlueViolet" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave"
SourceName="canvas">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="White" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Label x:Name="label" VerticalAlignment="Top" Height="100" BorderBrush="Black" BorderThickness="2" Content="LABEL"/>
<Canvas x:Name="canvas" Height="100" VerticalAlignment="Bottom"
IsHitTestVisible="True"
Background="AntiqueWhite"
/>
</Grid>
Can someone explain this behavior ?
You're running into the order of precedence of value sources for Dependency Properties. A common case of this is when you set a local value directly on an element, a value set in a style is overridden. In this case, you're applying an animation to the property, which takes precedence over anything set in the Style (or even a local value).
To allow the Style to take over again you need to make the animation no longer apply to the Label. You can do this by explicitly removing the initial animation, which will reset back to the original state, like a Property Trigger does:
<EventTrigger RoutedEvent="FrameworkElement.MouseEnter"
SourceName="canvas">
<BeginStoryboard x:Name="GridMouseover">
<Storyboard>
<ColorAnimation To="BlueViolet" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseLeave"
SourceName="canvas">
<RemoveStoryboard BeginStoryboardName="GridMouseover"/>
</EventTrigger>
The disadvantage of this is that you lose the smooth animation back to White. VisualStateManager is a much better choice for this kind of thing in many cases because it handles that for you automatically.
The other thing you can do is tell the Storyboard to stop applying itself after finishing by changing the FillBehavior:
<EventTrigger RoutedEvent="FrameworkElement.MouseEnter"
SourceName="canvas">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="BlueViolet" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseLeave"
SourceName="canvas">
<BeginStoryboard>
<Storyboard FillBehavior="Stop">
<ColorAnimation To="White" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>

How do I use content size in Button Template Triggers?

I'm creating a ControlTemplate for a Button. I want to animate the size so it grows to fit the content when the mouse hovers over it. However, I'm not sure how to get the content size in the triggers section. Here is what I have.
<ControlTemplate x:Key="buttonTemplate" TargetType="Button">
<Grid Name="grid" Height="12" Width="12" Opacity="0.4">
<ContentPresenter Name="content" ... />
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Width"
To="40"
Duration="0:0:0.4"/>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Height"
To="20"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Width"
Duration="0:0:0.2"/>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Height"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I've tried using TemplateBinding in place of the To="40" in the trigger, but that causes exceptions. How can I use dynamic values in the triggers?
Rather than animating the layout sizes directly you can apply a ScaleTransform to grid and animate its ScaleX and ScaleY values. This will not only adjust automatically for whatever the size is but can also provide better performance, especially if you apply it as a RenderTransform instead of a LayoutTransform. If there are other changes to the layout that you're expecting to happen during the animation you can stick with LayoutTransform but it will be slower as it needs to continually recalculate layout measurements and arrangement.

Resources