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
Related
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.
I am attempting to animate the background colour of a label on the data change of a property. The label is changing colour up to the value of 3. however if the property returns to a value of 1, the animation stays on the previous colour. My XAML is below.
<Style x:Key="Colors" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Red"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Yellow"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="3">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Green"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Label XAML
<Label x:Name="vClock" Content="{Binding Path=Clock,Mode=OneWay}" FontSize="25" Style="{StaticResource Colors}">
</Label>
I think you need to remove the other active storyboard first, but it's is better to proceed declaring your storyboard in the resources:
<Storyboard x:key="Value 1">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Red"/>
</Storyboard>
<Storyboard x:key="Value 2">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Yellow"/>
</Storyboard>
<Storyboard x:key="Value 3">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Green"/>
</Storyboard>
then look at the datatrigger
<Style x:Key="Colors" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="1">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Value2" />
<StopStoryboard BeginStoryboardName="Value3" />
<BeginStoryboard Storyboard="{StaticResource Value1}"
x:Name="Value1" />
</DataTrigger.EnterActions>
</DataTrigger>
...
</Style.Triggers>
</Style>
Do the same for all the triggers
I want to use an image to show state and I want one of those images to be a loading spinner. I'm rotating the image when its a spinner using a trigger. However, this approach seems to throw the error "Cannot animate '(RenderTransform).(RotateTransform.Angle)' on an immutable object instance"
<ItemsControl ItemsSource="{Binding StatefulViewModels}"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Expander IsEnabled="{Binding IsCompleted}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<Image Margin="5" Width="18">
<Image.RenderTransform>
<RotateTransform Angle="0" CenterX="9" CenterY="9"/>
</Image.RenderTransform>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedWithErrors}">
<Setter Property="Source" Value="../Images/Failed.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedSuccessfully}">
<Setter Property="Source" Value="../Images/Success.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Unexecuted}">
<Setter Property="Source" Value="../Images/Waiting.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Running}">
<Setter Property="Source" Value="../Images/Running.png"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:2"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Margin="5" Text="{Binding Path=Title}" FontSize="16"/>
</StackPanel>
</Expander.Header>
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ResultTemplateSelector}"/>
</Expander>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've read an MSDN article explaining about datatriggers and storyboards but I can't think of a good alternative to this approach.
Whats the correct way to achieve this/fix my approach?
Try to set the RenderTransform property of the Image using a Style setter instead of setting it using a local value:
<Image Margin="5" Width="18">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="0" CenterX="9" CenterY="9"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedWithErrors}">
<Setter Property="Source" Value="../Images/Failed.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedSuccessfully}">
<Setter Property="Source" Value="../Images/Success.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Unexecuted}">
<Setter Property="Source" Value="../Images/Waiting.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Running}">
<Setter Property="Source" Value="/Images/Running.png"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:2"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
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.
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>