WPF animation that is always running when the control is visible - wpf

I have a (lame) user requirement to make a control super visible.
Sadly that means a flashing background (Ug).
So, the control is a Border that holds a TextBlock is only visible in fairly rare scenarios.
I have looked at a few animation examples and they all have a "Trigger" on them. Most commonly when the user clicks on something.
Is there a way to just have the animation running all the time (if the control is visible of course)?

here you go, RepeatBehavior="Forever" will keep the animation running until stopped or removed
you can trigger a color animation with auto reverse enabled on the control load and let it run forever
<Border Background="Transparent">
<TextBlock Text="some text" />
<Border.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="SkyBlue"
Storyboard.TargetProperty="Background.Color"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
if you need the animation to be triggered on visibility change then here is a way, note that the animation is applied when IsVisible property become true and stopped when it become false.
<Border Background="Transparent">
<TextBlock Text="some text" />
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsVisible"
Value="true">
<Trigger.EnterActions>
<BeginStoryboard x:Name="startFlashing">
<Storyboard>
<ColorAnimation To="SkyBlue"
Storyboard.TargetProperty="Background.Color"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="startFlashing" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
typically after visibility is set to false there is no visible difference if animation is still running or stopped.

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>

WPF animation does not trigger iff animating Opacity from 0.0

I would like to fade in/out animate FontAwesome.WPF spinner icon when it starts/stops spinning. Luckily, MSDN provides almost identical example how to Trigger an Animation on Property Change which uses IsMouseOver. Chaning the property to icon's Spin and opacity to 0.0 should be straightforward:
<CheckBox x:Name="IsExecuting"/>
<fa:ImageAwesome xmlns:fa="http://schemas.fontawesome.io/icons/"
Icon="Refresh" Height="8" Width="8"
Spin="{Binding IsChecked, ElementName=IsExecuting}">
<fa:ImageAwesome.Style>
<Style TargetType="{x:Type fa:ImageAwesome}">
<Setter Property="Opacity" Value="0.0" />
<Style.Triggers>
<Trigger Property="Spin" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</fa:ImageAwesome.Style>
</fa:ImageAwesome>
But it does not work - nothing happens at all, spinner is invisible regardless of Spin property value. Curiously, the fade in/out works flawlessly, as long as the 'hidden' Opacity is not 0, i.e. if I rolled back to 0.25 as in MSDN example, it would work.
What is the problem here and how can I fix it?

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.

Border of the control is not displayed at appropriate time

I am trying to create a designer like Visual Studio.
Suppose I have a Grid.
Inside that I have a TextBox and a TextBlock. For better understanding look at the sample code below:
<Page.Resources>
<Style x:Key="myStyle" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<Border Style="myStyle">
<Grid>
<Border Style="myStyle">
<TextBox ...... />
</Border>
<Border Style="myStyle">
<TextBlock ...... />
</Border>
</Grid>
</Border>
Now when I mouseOver on any of the element I want to get a border around it.
My Problems:
I get a border around grid as well as a border around textblock when mouse cursor is over textblock.
When my mouse cursor goes over empty area of grid the border is not shown.
Requirements :
when mouse cursor goes over textblock, the border around grid should become invisible.
when mouse cursor goes over empty area in grid, the border around grid should become visible.
Please suggest the changes that I should make in the above code to have the required functionality.
Why did you assign myStyle to the outter Border? Just leave that out.
1: I think, your idea with glowing parent border is not good. Because it will be blink whenever user hold mouse over the grid. Perhaps, it will annoy user :)
2: Try to set Grid.Background="Transparent".
Try this
<Grid Background="LightGray" >
<Grid.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Grid_Bd" Duration="0:0:0.1"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Grid_Bd" Duration="0:0:0.1"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid Height="50" Width="50">
<Border x:Name="TextBlock_Bd" Opacity="0" BorderBrush="Blue" BorderThickness="1"/>
<TextBlock Text="Hello !!" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="TextBlock_Bd" Duration="0:0:0.1"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation From="1" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="TextBlock_Bd" Duration="0:0:0.1"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Grid>
<Border x:Name="Grid_Bd" Opacity="0" BorderBrush="Red" BorderThickness="1"/>
</Grid>

Trigger on MouseHover?

I was wondering if there was a way to setup a trigger on MouseHover, not MouseOver.
I would like an action to occur when the user keeps their mouse over a specified area for X seconds instead of when they just MouseOver it in passing.
To make the IsMouseOver trigger begin after X seconds, you should be able to use a Storybard and set the BeginTime Property. Here's an example for a Button which increases its size by 50% when the mouse is hovering it for 2 seconds.
To skip the animation part you can set Duration="0" for the DoubleAnimations
<Button Content="Button"
Height="23"
Width="75"
RenderTransformOrigin="0.5 0.5">
<Button.RenderTransform>
<ScaleTransform />
</Button.RenderTransform>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard BeginTime="00:00:02">
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleX)" To="1.5"/>
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleY)" To="1.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleX)" To="1.0"/>
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleY)" To="1.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Resources