please look at this xaml code:
<Style x:Key="WowButton" TargetType="Button" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Width" Value="300"></Setter>
<Setter Property="FontSize" Value="30"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Yellow"></Setter>
<Setter Property="Width" Value="150"></Setter>
<Setter Property="FontSize" Value="25"></Setter>
</Trigger>
<EventTrigger RoutedEvent="Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Width"
From="300" To="500 ">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:2" Storyboard.TargetProperty="(Button.RenderTransform).(RotateTransform.Angle)"
From="0" To="360" RepeatBehavior="1x">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
I used this style on a button in the Main Window.
How is that that when I leave the button it rotates and it's width is getting smaller, but after I click on it - it's width doesn't change back?
From MSDN:
In some cases, it might appear that you can't change the value of a
property after it has been animated.
You can solve this by adding new Storyboards to change Width property:
<Style x:Key="WowButton" TargetType="Button" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontSize" Value="30"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Yellow"></Setter>
<Setter Property="Width" Value="150"></Setter>
<Setter Property="FontSize" Value="25"></Setter>
</Trigger>
<EventTrigger RoutedEvent="Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Width"
From="300" To="500 ">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0" Storyboard.TargetProperty="Width" From="150" To="300"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:2" Storyboard.TargetProperty="(Button.RenderTransform).(RotateTransform.Angle)"
From="0" To="360" RepeatBehavior="1x">
</DoubleAnimation>
<DoubleAnimation Duration="0:0:0" Storyboard.TargetProperty="Width" From="300" To="150 "/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Unlike Trigger's basic setter's, Storyboard's do not get an auto RemoveStoryboard call to remove them.
Thus it's upto you to get rid of them when they do not apply anymore. If you were using a Trigger then you can use Trigger.EnterActions and Trigger.ExitActions to do the apply and remove of Storyboard's. This does not exist for EventTrigger's.
In your case Just remove the click Storyboard on the MouseLeave event and vice-versa.
something like: (note we have added a x:Name for the BeginStoryboard's)
<Style x:Key="WowButton"
TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Width"
Value="300" />
<Setter Property="FontSize"
Value="30" />
<Setter Property="FontWeight"
Value="Bold" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="False">
<Setter Property="Background"
Value="Yellow" />
<Setter Property="Width"
Value="150" />
<Setter Property="FontSize"
Value="25" />
</Trigger>
<EventTrigger RoutedEvent="Click">
<BeginStoryboard x:Name="clickStoryboard">
<Storyboard>
<DoubleAnimation Duration="0:0:0.25"
From="300"
Storyboard.TargetProperty="Width"
To="500 " />
</Storyboard>
</BeginStoryboard>
<RemoveStoryboard BeginStoryboardName="leaveStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard x:Name="leaveStoryboard">
<Storyboard>
<DoubleAnimation Duration="0:0:2"
From="0"
RepeatBehavior="1x"
Storyboard.TargetProperty="(Button.RenderTransform).(RotateTransform.Angle)"
To="360" />
</Storyboard>
</BeginStoryboard>
<RemoveStoryboard BeginStoryboardName="clickStoryboard" />
</EventTrigger>
</Style.Triggers>
</Style>
You don't have to create new Storyboard's to remove old one's. Just use RemoveStoryboard's appropriately to get rid of em.
Related
I need define simple animation in the XAML (without code behind), that must rotate the background image of button by changing the bounded boolean property. I have a button in the XAML:
<Button Style="{StaticResource btnStyle}" />
In the Resources section of the Window I create following:
<RotateTransform Angle="180" x:Key="rotAt180" />
<Style TargetType="Button" x:Key="btnStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Source="Images\pic.png" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding MyBoolProp}" Value="False">
<Setter Property="RenderTransform" Value="{StaticResource rotAt180}" />
<!-- This animation works good -->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="180" Duration="0:0:0.2" Storyboard.TargetProperty="RenderTransform.Angle" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<!-- This animation causes exception -->
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="180" To="0" Duration="0:0:0.2" Storyboard.TargetProperty="RenderTransform.Angle" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
MyBoolProp is initialized by True.
The first animation is works perfect. But the second animation causes System.InvalidOperationException in PresentationFramework.dll ("Unable to resolve all property references in the "RenderTransform.Angle" property path. Check that the corresponding objects support such properties").
I tried to move the second animation into the trigger, where MyBoolProp is True:
<DataTrigger Binding="{Binding MyBoolProp}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="180" To="0" Duration="0:0:0.2" Storyboard.TargetProperty="RenderTransform.Angle" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
But the result is the same. What is wrong in such realization?
The RenderTransform must be set in the Style, not in the DataTrigger:
<Style TargetType="Button" x:Key="btnStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Source="Images\pic.png" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="RenderTransform" Value="{StaticResource rotAt180}"/>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
<Style.Triggers>
<DataTrigger Binding="{Binding MyBoolProp}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:0.2"
Storyboard.TargetProperty="RenderTransform.Angle" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="180" Duration="0:0:0.2"
Storyboard.TargetProperty="RenderTransform.Angle" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
I have a ListBox with customized item's style. I want the item to grow a bit when selected, and go back to its original size when deselected. I've tried several solutions but none seemed to work. I think the problem lies in the proper setting of Storyboard.TargetProperty.
My current XAML looks like this:
...
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.(ScaleTransform.ScaleX)" To="1.2" Duration="0:0:.3" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
...
My final code (with answers applied):
...
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform />
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" To="1.1" Duration="0:0:.1" />
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" To="1.1" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" To="1.0" Duration="0:0:.1" />
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" To="1.0" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
...
Try using following code:
...
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform x:Name="scaleTransform" />
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleX" To="1.2" Duration="0:0:.3" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
...
I have a textblock that currently has a trigger that sets the foreground colour when the mouse enters and back to the default when it leaves. The problem I have is that I would also like the mouse pointer to change I currently have the following
<Style TargetType="TextBlock" x:Key="FlatStyleButton">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#FF333333" />
<Style.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Foreground.Color" To="CornflowerBlue" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Foreground.Color" To="White" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
I have tried adding <Setter Property="Cursor" Value="Hand"></Setter> to various places but it never seems to work
Sorry guys Proper school boy error on my part im afraid, what I was trying would have worked but I was modifiying in the wrong resource file. So if anyone else is intrested the answer was:
<Style TargetType="TextBlock" x:Key="FlatStyleButton">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#FF333333" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
</Trigger>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Foreground.Color" To="CornflowerBlue" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Foreground.Color" To="White" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
I am trying to move as well as scale an Image on mousemove/source changed of the image. I have written following style for the Image, but it is not working :
<Style x:Key="MovingImage" TargetType="{x:Type Image}">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Stretch" Value="Uniform"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Width" Value="auto"/>
<Setter Property="Height" Value="auto"/>
<Style.Resources>
<Storyboard x:Key="TransformImage">
<DoubleAnimation Duration="0:0:3" By="-500" Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)"></DoubleAnimation>
<DoubleAnimation Duration="0:0:3" By="-500" Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.Y)"></DoubleAnimation>
<DoubleAnimation Duration="0:0:3" By="-500" From="1" To="0.5" Storyboard.TargetProperty="(Image.RenderTransform).(ScaleTransform.ScaleX)"></DoubleAnimation>
<DoubleAnimation Duration="0:0:3" By="-500" From="1" To="0.5" Storyboard.TargetProperty="(Image.RenderTransform).(ScaleTransform.ScaleY)"></DoubleAnimation>
</Storyboard>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource TransformImage}" />
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
You need to do two things to get this working. Firstly define the default transform group that you want to have set (so in your case the appropriate transforms inside a transform group)
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<TranslateTransform />
<ScaleTransform />
</TransformGroup>
</Setter.Value>
</Setter>
Then next when you actually reference the target property you have to explicitly specify the point of the transform group that you want to change. I would also suggest adding in some exit actions so your image doesn't disappear forever =)
<DoubleAnimation Duration="0:0:3" By="-500" Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"></DoubleAnimation>
<DoubleAnimation Duration="0:0:3" By="-500" Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)"></DoubleAnimation>
<DoubleAnimation Duration="0:0:3" By="-500" From="1" To="0.5" Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"></DoubleAnimation>
<DoubleAnimation Duration="0:0:3" By="-500" From="1" To="0.5" Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"></DoubleAnimation>
It seems like there has to be a way to do this:
I am applying an ItemContainerStyle in my Listbox, based on two property triggers. As you can see, I'm using the exact same set of trigger enter/exit actions, simply applied on two different properties. Is there something equivalent to a <Trigger Property="prop1" OR Property="prop2"> ??? (Obviously wouldn't look like that, but that probably gets the point across.)
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="50" Duration="0:0:.3"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="25" Duration="0:0:.3" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="50" Duration="0:0:.3"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="25" Duration="0:0:.3" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
Have you tried any of the following (extracted from Adam Nathan's book: Windows Presentation Foundation Unleashed):
Multiple triggers applied to the same element (to get a logical OR).
Multiple properties evaluated for the same trigger (to get a logical AND).
Logical OR
Since Style.Triggers can contain multiple triggers, you can create more than one with the exact same Setters to express a logical OR relationship.
For example:
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
...
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
....
</Trigger>
</Style.Triggers>
This means, "if IsMouseOver is true or if IsKeyboardFocusWithin is true, apply the action.
Logical AND
To express a logical AND relationship, you can use a variation of Trigger called MultiTrigger, or a variation of DataTrigger called MultiDataTrigger. Both triggers have a collection of Conditions that contain the information you would normally put directly inside a Trigger or DataTrigger.
For example:
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsKeyboardFocusWithin" Value="True"/>
</MultiTrigger.Conditions>
</MultiTrigger>
<Setter ...>
<Setter ...>
</Style.Triggers>
You can put the Storyboards into resources and just reference them in the triggers. It's not exactly what you want, but it allows you to define your animations in one central place (rather than having to copy & paste them).
<Style TargetType="...">
<Style.Resources>
<Storyboard x:Key="MyGetFocusAnimation">
<DoubleAnimation Storyboard.TargetProperty="Height"
To="50" Duration="0:0:.3" />
</Storyboard>
<Storyboard x:Key="MyLoseFocusAnimation">
<DoubleAnimation Storyboard.TargetProperty="Height"
To="25" Duration="0:0:.3" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyGetFocusAnimation}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MyLoseFocusAnimation}" />
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyGetFocusAnimation}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MyLoseFocusAnimation}" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>