WPF DoubleAnimation does not decrease the value after an animation - wpf

Consider the following simple WPF form, we will try to animate border1's Height:
This is the XAML for border1:
<Border Margin="3" BorderBrush="Black" BorderThickness="1" Name="border1">
<Border.Style>
<Style>
<Setter Property="Control.Height" Value="50" />
<Style.Triggers>
<DataTrigger Binding="{Binding X}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="100" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding X}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="200" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding X}" Value="3">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="300" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding X}" />
</Border>
X is a normal DependencyProperty and buttons do their work without problem and TextBlock's content inside border1 shows the changed value.
Now the problem is Height can be only Increased! For example by pressing 1, Height increases to 100, pressing 3 increases it to 300 but pressing 2 does not change the height.
If I set the initial border1's height to, for example, 400, all buttons can decrease it to 100, 200 or 300 but after this stage no animation can decrease border's height.
Am I missing some obvious point regarding WPF animation?

Update
Part of the issue is the storyboard in the DataTrigger is not being stopped, so (the last DataTrigger defined) is always holding the Height's 'animated' value.
Adding a StopStoryboard in the exit action fixes part of it. You can now switch between all three heights again... but the animation always starts from the Height's base value (which is clearly not what you want). You want it to start from where it was last set at.
The following MSDN article explains the second issue pretty well:
http://msdn.microsoft.com/en-us/library/aa970493.aspx
Good Solution- EventTriggers on your three buttons seem to work better and doesn't have the problems that the DataTrigger encounters (plus you don't even need the dependency property 'X' anymore!)
<Border Margin="3" BorderBrush="Black" BorderThickness="1" Name="border1">
<Border.Style>
<Style>
<Setter Property="Control.Height" Value="50" />
</Style>
</Border.Style>
<TextBlock Text="{Binding X}" />
</Border>
and
<StackPanel Orientation="Horizontal">
<Button Content="1" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="100"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="2" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="200"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="3" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="300"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>

There must be a simpler way to do this, but I cannot recall if there is at the moment.
Otherwise this will work. You can set the ExitAction of your triggers to set back to the default value. Do this by using a DoubleAnimation with no To value.
i.e.,
<DataTrigger Binding="{Binding X}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.2" To="100" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>

Related

WPF XAML Animation How do i stop it when bound boolean becomes false?

I want the animation to stop when the boolean CanAnimate becomes false. It starts on true, so how do i tell it to stop when CanAnimate is false? (The CanAnimate bool is set inside a SelectedItem setter)
<Border BorderBrush="Black" BorderThickness="2" Margin="1" Name="ReviewNote">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding CanAnimate}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0" To="0.0" AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock/>
</Border>
You can try using the ExitActions on the DataTrigger to stop the animation, by overriding with another animation. For instance:
<DataTrigger Binding="{Binding CanAnimate}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0" To="0.0" AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Opacity)"
From="0.0" To="0.0" Duration="0:0:0.0" FillBehavior="HoldEnd" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
Alternatively, there is a way to stop storyboards in XAML by name, again you could use ExitActions for this. This previous question shows the way.
Hope this helps!

How to write a style for a control that changes width of another control?

Is there any way to write a Style for a control that changes width of another control?
<Style x:Key="SubMenuStyle" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="LightCyan"/>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Menu" Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
This code leads to error:
TargetName property cannot be set on a Style Setter
I know I can write codes below and it works:
<Label Name="Owners" Margin="0,1,0,0" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}">
<Label.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Menu" Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Label.Triggers>
</Label>
But since I use this trigger in multiple labels I want to write it in a style once.
This is my code to define controls:
<Border Name="Menu" Grid.Column="2" Grid.Row="1" Width="0" HorizontalAlignment="Right" BorderBrush="LightBlue" BorderThickness="2" CornerRadius="2" Background="LightCyan">
<StackPanel Name="MenuPanel">
<Button Style="{StaticResource MenuButtonsStyle}">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ListsMenu" Storyboard.TargetProperty="Height" To="86" Duration="0:0:.6"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<StackPanel Name="ListsMenu" Height="0">
<Label Name="Owners" Margin="0,1,0,0" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
<Label Name="Contacts" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
<Label Name="Groups" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
</StackPanel>
</StackPanel>
</Border>
Scenario: There is a border, It's default width is zero, It will raise to 165 in another trigger which works fine, I want to animate it to zero again when labels are clicked, but I can not access the width of that border in lables style
You claim that you use triggers in multiple labels so you can define trigger one time like this
<Storyboard x:Key="animation">
<DoubleAnimation Storyboard.TargetName="ListsMenu" Storyboard.TargetProperty="Height" To="86" Duration="0:0:.6"/>
</Storyboard>
and later when you need you can call it
<EventTrigger RoutedEvent="MouseEnter" >
<BeginStoryboard Storyboard="{StaticResource animation}"/>
</EventTrigger>
But I'm a bit confused what you meant to acquire.
You cannot get access in style since style does not have a namescope, as it was said. If it changes label's width you could do this in style like this
<Style x:Key="style_button" TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="300" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
but it invokes by StoryBoard.TargetName to another object.
try :
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.Target="{Binding ElementName=Menu}"
Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
TargetName is only valid inside Templates.

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>

EventTrigger in XAML when a property changes?

I have this code (it's working, so far);
<Image Name="img_person" Tag="{Binding Path=MyProperty1, NotifyOnTargetUpdated=True}" Canvas.Top="0" Canvas.Left="0">
<Image.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="img_person"
Storyboard.TargetProperty="(Canvas.Top)"
By="64"
Duration="0:0:0.8"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
I'd like to change this line;
<EventTrigger RoutedEvent="Binding.TargetUpdated">
To something like this;
<EventTrigger ThisParticularPropertyHasChanged="MyProperty1">
I want to do this as I'll have several storyboards, each to be started when a particular property changes. With 1 property I've been binding it to the image's Tag property, but now that I have several to do (roughly 8) I'm not sure how to go about this.
The class implement INotifyPropertyChanged.
thanks.
SOLUTION, thanks to sa_ddam213;
<Style x:Key="PersonImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="South">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="64" Duration="0:0:0.8" AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="East">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="64" Duration="0:0:0.8" AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>

Problem animating a WPF button

I have a button, this is a unique button, and his style should çn't match the style of all the others. So when you pass your mouse over this button, it should change its image. But it is not working, Here is the code... I'm starting at WPF so if you can point me what am I doing wrong would be really appreciated
<Button Name="RemoveButton" ClickMode="Press" BorderThickness="0" Background="Transparent" Style="{StaticResource ButtonStyle1}">
<Button.Content>
<Grid>
<Image x:Name="CloseActive" x:FieldModifier="public" Height="12" VerticalAlignment="Center" Source="/HTFS.Atlas.Portfolio.PortfolioClient.WCF.Controls;component/Images/tab-close.png" Visibility="Hidden" />
<Image x:Name="CloseInactive" x:FieldModifier="public" Height="12" VerticalAlignment="Center" Source="/HTFS.Atlas.Portfolio.PortfolioClient.WCF.Controls;component/Images/tab-close-inactive.png" />
</Grid>
</Button.Content>
<Button.Resources>
<Storyboard x:Key="MouseOverAnimation">
<DoubleAnimation Storyboard.TargetName="CloseActive" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="CloseInactive" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5" />
</Storyboard>
<Storyboard x:Key="MouseOutAnimation">
<DoubleAnimation Storyboard.TargetName="CloseInactive" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="CloseActive" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5" />
</Storyboard>
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Control}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MouseOverAnimation}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MouseOutAnimation}" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Resources>
</Button>
For the CloseActive image, instead of using Visibility="Hidden" try Opacity="0".
In your animation you're adjusting the opacity of the image, but it's still hidden.
In addition to removing the visibility attribute, try putting the animation triggers directly in Button.Triggers
<Button.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource MouseOverAnimation}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource MouseOutAnimation}" />
</EventTrigger>
</Button.Triggers>
since you can't use Storyboard.TargetName in a storyboard called from a style. You'll also have to use event triggers instead of normal triggers.
You can also remove the CloseButtonStyle since it won't be used.

Resources