Image Transformation with ResourceDictionary - wpf

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>

Related

Why XAML defined animation causes System.InvalidOperationException

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>

Extend the style to capture click event

I am trying to build a designer like visual studio.
Take a look at the xaml:
<Style x:Key="myStyle" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
</Style.Triggers>
</Style>
...
...
...
<Border Style="myStyle">
<Grid>
<Border Style="myStyle">
<Rectangle Fill="Transparent" />
<TextBlock Text="abc" />
</Border>
</Grid>
</Border>
The above code is working perfectly. Now I want to extend the above style, such that the border's color should change to green when I click on any of the control.
Update :
I have changed the above style to something like below code.
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<EventTrigger RoutedEvent="MouseDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="Green" Duration="0:0:0.100" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
Now I can see the border is changing its color to green when I click on it. But when mouse leaves the textblock, the border changes its color back to transparent.
<Style.Triggers>
<EventTrigger RoutedEvent="MouseDown">
<Setter Property="BorderBrush" Value="DodgerBlue" />
</EventTrigger>
</Style.Triggers>
and you dont want to bind the style for all the border, if you need to apply that style for all the border, just remove the key name of that style. It will apply to all the Targeted Control.
If you need to check and apply the color as like the button click as like a Toggle button, you need to maintain a property and that property is to be given in the trigger and it need to be changed in the button's click..
Try this
<Style x:Key="myStyle" TargetType="{x:Type Border}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush">
<Setter.Value>
<SolidColorBrush x:Name="BorderBrushColor" Color="Transparent"></SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<EventTrigger RoutedEvent="MouseDown">
<BeginStoryboard>
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Green" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Try this
<Style x:Key="myStyle" TargetType="{x:Type Border}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush">
<Setter.Value>
<SolidColorBrush x:Name="BorderBrushColor" Color="Transparent"></SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimationUsingKeyFrames FillBehavior="HoldEnd" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="DodgerBlue" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseDown">
<BeginStoryboard>
<Storyboard>
<ColorAnimationUsingKeyFrames FillBehavior="HoldEnd" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Green" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Add this code in previous style
<Trigger Property="IsMouseOver" Value="False">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimationUsingKeyFrames FillBehavior="HoldEnd" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Transparent" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>

ListBoxItem in WPF - scale animation when selected

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>
...

How do I write WPF Trigger to change cursor on textblock hover

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>

Triggers behavior with actions

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.

Resources