WPF : Animating Width of an Control alternately with start value - wpf

I have an image whose width should be animated from 0 to 30 pixels when a boolean property changes to true an back from 30 to 0 pixels if the property switches back to false.
In general, this works with this XAML code:
<Image.Style>
<Style TargetType="{x:Type Image}">
<!-- <Setter Property="Width" Value="0"/> -->
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=AIM, Path=IsDeletingEnabled}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Animate the Width of the Image from 0 to 30px within 300ms-->
<DoubleAnimation Storyboard.TargetProperty="Width" From="0" To="30" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<!-- Animate the Width of the Image from 30 to 0px within 300ms-->
<DoubleAnimation Storyboard.TargetProperty="Width" From="30" To="0" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
The problem is, that before the Property IsDeletingEnabled switches to true the first time, the images witdh is 30px and it is visible. When I uncomment the first setter and set the Witdh to 0px by default (or the Visibilityto Hidden), the image isn't shown at startup but disappears instantly after switching IsDeletingEnabled from trueto fals. In this case, no animation is visible.
Has anywone a solution how I can set the Visibilityto Hiddenor the Witdhto 0px at startup without hiding the DataTrigger.ExitAction ?

Do you explicitly need to set the Visibility to Hidden if you're setting the Width to 0? A zero-width element and a hidden element look the same onscreen.
Your exit action is not being 'hidden', what's happening is the DataTrigger is 'undoing' the <Setter Property="Visibility" Value="Visible"/> in addition to running the exit action. But since the visibility has immediate effect you don't see the exit action being run.
Your enter action needs to both Visibility=Visible and start the expanding animation. Your exit action needs to perform the shrinking animation, but wait to set Visibility=Visible until after it's complete. You can do this with an ObjectAnimationUsingKeyFrames animation:
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Hidden"/>
<Setter Property="Width" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsShown}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Animate the Width of the Image from 0 to 30px within 300ms-->
<DoubleAnimation Storyboard.TargetProperty="Width" From="0" To="30" Duration="0:0:0.3"/>
<!-- Set the Image to Visible immediately -->
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<!-- Animate the Width of the Image from 30 to 0px within 300ms-->
<DoubleAnimation Storyboard.TargetProperty="Width" From="30" To="0" Duration="0:0:0.3" />
<!-- After 300ms, hide the Image -->
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0.3" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Hidden</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>

Related

Visibility fadeout and fade in infinite loop

I have some Grid with some element inside and I want to add it some fade in fade out effect with Visibility property in infinite loop base on some bool property.
This is what I have tried:
<Grid.Style>
<Style TargetType="FrameworkElement">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Opacity" Value="0"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Collapsed">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
Currently I cannot see my Grid at all.
There are a few issues in your style:
You do not see the Grid, because you set the initial Opacity to 0.
You can remove both of your Setters, because those are the default values
Making the trigger dependent on Visibility does not work, because when it is set to Collapsed, the Grid is already invisible, so the animation is not even visible.
There is no exit action to make the Grid fade in again.
You could solve the issues by exposing a property (here MyAnimationTriggerProperty) that you use as trigger for the animation. Depending o your actual application this could also be the IsChecked state of another control. Then you can add an ObjectAnimationUsingKeyFrames to set the Visibility at the end of the animation.
<Style TargetType="FrameworkElement">
<Style.Triggers>
<DataTrigger Binding="{Binding MyAnimationTriggerProperty}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.0"
Duration="0:0:0.2"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0.2" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:0.2"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>

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?

Fade out animation on DockPanel

<DockPanel Name="MyPanel" IsVisibleChanged="MyPanel_IsVisibleChanged">
<DockPanel.Triggers>
<EventTrigger RoutedEvent="IsVisibleChanged"> // error here
</EventTrigger>
</DockPanel.Triggers>
</DockPanel>
Above is my dockpanel xmal code. Because IsVisibleChanged is not a RoutedEvent I can not add in the EventTrigger this code:
<Storyboard x:Key="hideMe">
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:2" To="0.0"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:2" Value="{x:Static Visibility.Hidden}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="showMe">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:5" To="0.75"/>
</Storyboard>
I try to give a fade out animation to my dockpanel.
Instead of using an event trigger, I would use a normal trigger to check the value of the Visibility property of the DockPanel.
You can create a style on the DockPanel to do it, like this:
<DockPanel Name="MyPanel">
<DockPanel.Style>
<Style TargetType="DockPanel">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<!-- Set storyboard to run when DockPanel is set visible here: -->
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<!-- Set storyboard to run when DockPanel is no longer set visible here: -->
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>

Bold changed cells in DataGrid

I would like to bold changed DataGrid cells when data source gets updated. I found that <EventTrigger RoutedEvent="Binding.TargetUpdated"> is the event I need. However I cannot get the Storyboard to work with the FontWeight property.
Here is what I am trying:
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<Int32Animation Duration="00:00:05"
Storyboard.TargetProperty="(Int32)(DataGridCell.FontWeight)"
From="400" To="700" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Can someone please recommend how to get the above fixed or propose new smarter way of bolding changed cells in DataGrid?
This works for me...
I have a resource...
<Style TargetType="TextBlock" x:Key="ElementStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Duration="00:00:01"
Storyboard.TargetProperty="(TextElement.FontWeight)">
<DiscreteObjectKeyFrame
KeyTime="00:00:00"
Value="{x:Static FontWeights.Thin}" />
<DiscreteObjectKeyFrame
KeyTime="00:00:00.5"
Value="{x:Static FontWeights.Heavy}" />
<DiscreteObjectKeyFrame
KeyTime="00:00:01"
Value="{x:Static FontWeights.UltraBold}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Assign this to relevant column,
<toolkit:DataGridTextColumn
Binding="{Binding Quantity, NotifyOnTargetUpdated=True}"
ElementStyle="{StaticResource ElementStyle}" />
EDIT
Bcause the code above makes the default values also Bold, we have another way to do this wherein ONLY the updates done by user would trigger boldness in the cell.
Style
<Style TargetType="Controls:DataGridCell"
BasedOn="{StaticResource {x:Type Controls:DataGridCell}}"
x:Key="CellBoldStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.SourceUpdated">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Duration="00:00:01" Storyboard.TargetProperty
="(TextBlock.FontWeight)">
<DiscreteObjectKeyFrame KeyTime="00:00:00.5"
Value="{x:Static FontWeights.Normal}" />
<DiscreteObjectKeyFrame KeyTime="00:00:01"
Value="{x:Static FontWeights.Bold}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Column
<Controls:DataGridTextColumn
Binding="{Binding Side, Mode=TwoWay,
NotifyOnTargetUpdated=True,
NotifyOnSourceUpdated=True}"
CellStyle="{StaticResource CellBoldStyle}" />
Limitations
Virtualized cells would loose the bold highlighted effect when you scroll them out of your scroll view.
Even your TextBox shows Bold value . (I dont know if this is a limitation for you!)

How can I make a XAML Trigger call a XAML Style?

This style correctly fades my toolbar in or out based on the changing of a ViewModel Property:
<Style x:Key="PageToolBarStyle" TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding PageToolBarVisible}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.0"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
However, when the application loads the status of the toolbar (faded out or in) is not in sync with the value of the ViewModel property (true or false). So I want to force this style to be executed upon loading the window, what is the syntax to do this, here is pseudo code of what I want to do:
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<EventTrigger.Actions>
<!-- PSEUDO-CODE: -->
<ExecuteTriggerStyle StyleToExecute="{StaticResource PageToolBarStyle}"/>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>

Resources