Storyboard animation datatrigger and reverse - c#

I have a observable collection of 10 objects binded in a itemscontrol canvas .
Each object has a different canvas.right canvas.top value.
The ideea was to click on the object to edit it , the object should move to a certain position , edit it and when its done click on it to go back to its original position.
So i used a checkbox to have a datatriger for the animation , i have two animations one for edit the other for going back.
Problem is that only the first animation fires the second never so the control never moves back to its position.
Here is the code:
<ItemsControl ItemsSource="{Binding SelectedButtonForEdit.Layers}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Canvas IsItemsHost="True"/>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Right" x:Name="right" Value="{Binding CanvasRight}"/>
<Setter Property="Canvas.Top" x:Name="top" Value="{Binding CanvasTop}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox Width="342" Height="156" IsChecked="{Binding IsEditing}" BorderBrush="Black" BorderThickness="1" Style="{StaticResource MaterialDesignRaisedAccentButton}" Background="{Binding Name,Converter={StaticResource LayerNameToColor}}" >
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="3*"/>
<RowDefinition Height="*"/>
</Grid>
</CheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditing}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Right)" Duration="0:0:0:0.1" ></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0:0.1" ></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsEditing}" Value="True">
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Right)" To="200" Duration="0:0:0:0.1" ></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" To="150" Duration="0:0:0:0.1" ></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

On your first trigger, you have a EnterActions, and on the second you have ExitActions. But in your case, you only need 1 trigger, with both EnterActions and ExitActions, like so :
<DataTrigger Binding="{Binding IsEditing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Right)" To="200" Duration="0:0:0:0.1" ></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" To="150" Duration="0:0:0:0.1" ></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Right)" Duration="0:0:0:0.1"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0:0.1"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
In the code I provided, when IsEditing will turn to True, the animation will move the object to 200,150, and when IsEditing will turn to false, will go back to the original values.

Related

Bind animations to ViewModel property

I've got a "flip card"-style animation that works just fine. On one side, it has a button that, when clicked, triggers the animation "FlipOpen":
<Button Content="click me" Height="130">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click" >
<BeginStoryboard Storyboard="{StaticResource FlipOpen}">
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
<Storyboard x:Key="FlipOpen">
<DoubleAnimation Duration="00:00:0.5" Storyboard.TargetProperty="RenderTransform.ScaleX" From="-1" To="1" Storyboard.TargetName="Back"/>
<DoubleAnimation Duration="00:00:0.5" Storyboard.TargetProperty="RenderTransform.ScaleX" From="1" To="-1" Storyboard.TargetName="Front"/>
</Storyboard>
<Storyboard x:Key="FlipClose">
<DoubleAnimation Duration="00:00:0.5" Storyboard.TargetProperty="RenderTransform.ScaleX" From="1" To="-1" Storyboard.TargetName="Back"/>
<DoubleAnimation Duration="00:00:0.5" Storyboard.TargetProperty="RenderTransform.ScaleX" From="-1" To="1" Storyboard.TargetName="Front"/>
</Storyboard>
I would like to bind the animation to a boolean property on my ViewModel and I have tried to use DataTriggers:
a) define a style:
<Style x:Key="myStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding FlipTheCard}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FlipOpen}"/>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding FlipTheCard}" Value="false">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FlipClose}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
b) apply it to both Grid-Containers that contain the front and back:
<Grid RenderTransformOrigin="0.5,0.5" x:Name="Back" Opacity="0" Style="{StaticResource myStyle}">
...
</Grid>
c) don't start the animation on button click, but bind a command that toggles the boolean to the button:
<Button Content="click me!" Command="{Binding ToggleFlipCard}" Height="130">
However, this results in an exception ("A storyboard-structure within a Style can not have a TargetName. Remove the TargetName").
What am I doing wrong here? How can I bind these animations to a boolean property on my ViewModel?
Thank you for any help!
I've also found a solution...it's far from perfect on the animation side (as I don't use keyframes and just scale the x property of both at the same time) but it should give you the idea:
Style:
<Storyboard x:Key="FlipOpen">
<DoubleAnimation Duration="0:0:0.3"
Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)"
To="0" />
</Storyboard>
<Storyboard x:Key="FlipClose">
<DoubleAnimation Duration="0:0:0.3"
Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)"
From="0"
To="1" />
</Storyboard>
<Style x:Key="GridStyle" TargetType="Grid">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="1" ScaleY="1" />
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FrontGridStyle" TargetType="Grid"
BasedOn="{StaticResource GridStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding FlipTheCard}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FlipOpen}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource FlipClose}" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="BackGridStyle" TargetType="Grid"
BasedOn="{StaticResource GridStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding FlipTheCard}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FlipClose}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource FlipOpen}" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
Grid:
<Grid>
<Grid Style="{StaticResource FrontGridStyle}">
<Border Width="100"
Height="100"
Background="Red" />
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding ToggleFlipCard}"
Content="click me!" />
</Grid>
<Grid Style="{StaticResource BackGridStyle}">
<Border Width="100"
Height="100"
Background="Green" />
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding ToggleFlipCard}"
Content="click me!" />
</Grid>
</Grid>
I found a solution.. yikes
reference Microsoft.Expression.Interactions.dll
define Interaction-Triggers:
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding FlipToFront}" Value="true">
<ei:ControlStoryboardAction Storyboard="{StaticResource FlipOpen}"/>
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding FlipToFront}" Value="false">
<ei:ControlStoryboardAction Storyboard="{StaticResource FlipClose}"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
credits

How to write a custom textbox in WPF with other components "glued" on it?

I have been for 2 days now trying to develop my own WPF textbox for no avail.
My initial try was inheriting from a control and then adding a template with all the stuff I wanted (a border and an asterisk in the outside).
Generic.xaml file:
<Style TargetType="{x:Type local:BdlTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BdlTextBox">
<Grid x:Name="ContentGrid">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="Tbl" Text="*" Foreground="Red" FontSize="15" Margin="0,-4,0,0"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:BdlTextBox}},
Path=IsRequired, Converter={StaticResource myBoolToVisibilityConverter}}"/>
<Rectangle Grid.Column="1" x:Name="Bg" Fill="Red" Opacity="0"/>
<TextBox Grid.Column="1" x:Name="Tb" Margin="1,1,1,1"/>
</Grid>
</Grid>
<ControlTemplate.Resources>
<Storyboard x:Key="flashAnimation">
<DoubleAnimation Storyboard.TargetName="Bg" Storyboard.TargetProperty="Opacity" From="0" To="1" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="2x" />
<DoubleAnimation Storyboard.TargetName="Bg" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:2"/>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=HasValidationError}" Value="True" >
<DataTrigger.EnterActions>
<BeginStoryboard Name="flash" Storyboard="{StaticResource flashAnimation}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="flash"/>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=HasValidationError}" Value="False" >
<Setter TargetName="Bg" Property="Opacity" Value="0"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is: I cannot use the control as a Textbox (the reason is obvious). I can't even properly use the Text property. So I was wondering what could be done to solve this problem.
Dependency Properties are also a problem. I have tried to "clone" the Text property from the Textbox "Tb", but failed too.

WPF XAML How can I conditionally perform this animation?

How do I conditionally get this animation to run, depending on a bool CanAnimate? It works right now, but I don't always want it to be animated, I want to check the boolean.
<Border BorderBrush="Black" BorderThickness="2" Margin="1" Name="ReviewNote">
<Border.Triggers>
<EventTrigger RoutedEvent="Border.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ReviewNote"
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0" To="0.0" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<TextBlock Text="{x:Static Constants:StringConstants.ReviewNote}"
Background="{StaticResource ReviewNoteColor}" Width="100"
TextAlignment="Center" />
</Border>
Apply style on your border and inside style you can check value of bool property. Based on that property you can specify DataTrigger.EnterActions with storyboard after removing TargetName from storyBoard.
This will work -
<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>

How to view border/popup for 5 seconds?

I want to animate a border like popup which will be visible for 5 seconds then collapse. When the border is visible, it will be only visible for 5 seconds and then collapse. This will be repeated everytime the border is visible. I have tried but it only appearing once. Can someone please explain to me the mistake in which I have made?
Here is the Style that i have implemented
<Grid.Resources>
<Style x:Key="NotificationStyle" TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard x:Name="NotificationStoryBoard">
<Storyboard >
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:05" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
Here is border...
<Border Name="NotifciationPopUp" Background="White" Height="80" Width="200" BorderBrush="Gray" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Visibility="Collapsed" Margin="10,0,0,10" Style="{StaticResource NotificationStyle}">
I realized hides the border in such a way:
Style:
<Style x:Key="NotificationStyle" TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" BeginTime="0:0:0" From="0.0" To="1.0" Duration="0:0:1.0" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" BeginTime="0:0:5" From="1.0" To="0.0" Duration="0:0:1.0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Grid:
<Grid>
<Border Name="MyBorder" Width="100" Visibility="Hidden" Height="50" BorderThickness="2" BorderBrush="OrangeRed" Style="{StaticResource NotificationStyle}">
<TextBlock Text="Some information" TextAlignment="Center" VerticalAlignment="Center" />
</Border>
<ToggleButton Name="MyButton" Content="Show border" Width="100" Height="30" VerticalAlignment="Bottom" Click="Button_Click" />
</Grid>
In code:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (MyButton.IsChecked == true)
{
MyBorder.Visibility = System.Windows.Visibility.Visible;
}
if (MyButton.IsChecked == false)
{
MyBorder.Visibility = System.Windows.Visibility.Hidden;
}
}
From his experiments I understood that if the property was once used in animation, then working with it is only possible from the animation-triggers. Accordingly, the access code of the property I have not received.
Besides, then the control's used Visibility=Collapsed, he it does not receive messages from the system and the animation will not work. Although a property "Hidden" should take messages, but I have not worked as well as animation, so in example I used opacity.

Load all the effect of storyboard until it's finish

I have an image which when pressed by user, the opacity will fade until value 0. But the effect only continues if user press and hold the image. Is there a possible way to change the code to: When user press, and without holding the press, the opacity fading effect will stay? I wish to do it in XAML.
<EventTrigger RoutedEvent="Button.Click" SourceName="press">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource showA}"/>
</EventTrigger.Actions>
</EventTrigger>
<Button Grid.Column="5" Command="{Binding Path=PressC}" CommandParameter="dot" Style="{StaticResource TransparentButton}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image Name="image5" Source="/W;component/Images/t.png" Height="100" />
<Image Name="pressed5" Source="/W;component/Images/T.png" Height="100" Width="200" Margin="-23,-136,-21,0" Visibility="Hidden" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="pressed5" Storyboard.TargetProperty="Opacity" Duration="0:0:1.5" From="1" To="0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Setter Property="Panel.ZIndex" Value="999"/>
<Setter TargetName="pressed5" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Can you show your full xaml? Because Image does not have IsPressed property and it is not clear how it works for you.
Similar code for button works as expected:
<Button Background="Aquamarine">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:1.5" From="1" To="0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
And for Image it works too with EventTrigger:
<Image Source="d:\123.png">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:1.5" From="1" To="0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Image.Style>

Categories