I'm trying to create an animated checkbox in WPF, similar to this one.
This question is NOT a duplicate of this one, because there is no problem with the framerate of the animation.
The animations and the storyboards are already in place, and working properly, but for some rason, while the animation is playing, it looks like the whole thing is shaking, as demonstrated here. The effect is best seen on the right border of the box.
The following is the XAML source code of the custom checkbox. I don't think that there is a need to post the code behind, since it doesn't contain anything other than the RoutedEvent definitions (FlatCheckBox.Checked and FlatCheckBox.Unchecked).
<UserControl x:Name="userControl" x:Class="Sync_Launcher.Controls.FlatCheckBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:Sync_Launcher.Controls"
mc:Ignorable="d"
d:DesignHeight="30"
Background="Transparent" MouseLeftButtonDown="FlatCheckBox_OnMouseLeftButtonDown">
<Grid SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ActualHeight, ElementName=userControl}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Column="0">
<Grid Name="tickHolderGridRoot" Margin="4">
<Grid.LayoutTransform>
<TransformGroup>
<RotateTransform x:Name="rotationTransform" Angle="0"/>
</TransformGroup>
</Grid.LayoutTransform>
<Grid Name="tickHolderGrid" Margin="0,0">
<Border Name="tickBorder" SnapsToDevicePixels="True" Opacity="0" BorderThickness="2,0,0,2" BorderBrush="#FF1CA36F" />
<Border Name="overlayBorder" SnapsToDevicePixels="True" Opacity="1" BorderThickness="2,2,2,2" BorderBrush="#FF404D61" />
</Grid>
</Grid>
</Grid>
<Label
Grid.Column="1"
Content="{Binding Text, ElementName=userControl}"
VerticalContentAlignment="Center"
FontSize="16"
Padding="5,0"
FontStyle="{Binding FontStyle, ElementName=userControl}"
FontWeight="{Binding FontWeight, ElementName=userControl}"/>
</Grid>
<UserControl.Resources>
<Duration x:Key="animationDuration">0:0:0.4</Duration>
<KeyTime x:Key="animationEnd">0:0:0.4</KeyTime>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger SourceName="userControl" RoutedEvent="controls:FlatCheckBox.Checked">
<BeginStoryboard>
<Storyboard Timeline.DesiredFrameRate="60">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="rotationTransform" Storyboard.TargetProperty="Angle" Duration="{StaticResource animationDuration}">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="{StaticResource animationEnd}" Value="-60">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="overlayBorder" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource animationDuration}"/>
<DoubleAnimation Storyboard.TargetName="tickBorder" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource animationDuration}"/>
<ThicknessAnimation Storyboard.TargetName="tickHolderGrid" Storyboard.TargetProperty="Margin" From="0,0" To="0,2" Duration="{StaticResource animationDuration}"/>
<ThicknessAnimation Storyboard.TargetName="tickHolderGridRoot" Storyboard.TargetProperty="Margin" From="4" To="2,0,2,4" Duration="{StaticResource animationDuration}"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger SourceName="userControl" RoutedEvent="controls:FlatCheckBox.Unchecked">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="rotationTransform" Storyboard.TargetProperty="Angle" Duration="{StaticResource animationDuration}">
<EasingDoubleKeyFrame KeyTime="0" Value="-60"/>
<EasingDoubleKeyFrame KeyTime="{StaticResource animationEnd}" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="overlayBorder" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource animationDuration}"/>
<DoubleAnimation Storyboard.TargetName="tickBorder" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource animationDuration}"/>
<ThicknessAnimation Storyboard.TargetName="tickHolderGrid" Storyboard.TargetProperty="Margin" From="0,2" To="0,0" Duration="{StaticResource animationDuration}"/>
<ThicknessAnimation Storyboard.TargetName="tickHolderGridRoot" Storyboard.TargetProperty="Margin" From="2" To="4" Duration="{StaticResource animationDuration}"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</UserControl.Triggers>
I have tried increasing the storyboard framerate by settings the Timeline.DesiredFrameRate to 60, as you can see in the code, but it had no effect on the shakyness of the animation.
I have also tried setting the SnapsToDevicePixels property to true hoping that it would improve the animation.
What might be the cause of this shaking effect, what can I do to eliminate it?
Related
I'm using C# WPF and have a Animation Style for the Border that is inside of a Grid that raise on MouseEnter event
<Style x:Key="Atash" TargetType="Border">
<Style.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.BlurRadius)" >
<EasingDoubleKeyFrame KeyTime="0" Value="12">
<EasingDoubleKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="37">
<EasingDoubleKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderThickness)" >
<EasingThicknessKeyFrame KeyTime="0" Value="5,3">
<EasingThicknessKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingThicknessKeyFrame.EasingFunction>
</EasingThicknessKeyFrame>
</ThicknessAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" >
<EasingColorKeyFrame KeyTime="0" Value="#FF4CAF50">
<EasingColorKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
<EasingColorKeyFrame KeyTime="0:0:0.3" Value="#FF4CAF50"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style>
What I need:
when I set the Style of my Border to Border x:Name="border2" Style="{StaticResource Atash}" The animation starts when the MouseEnter event occurs on the Border itself (mouse on the border).
But I want my animation to be executed in all the events that I want according to several specified controls.
I mean that I can set this style on the grid as well : Grid Style="{StaticResource Atash}"
Why,
because I want this animation to run when the mouse is on the grid, not just for the border
Long story short : I want to do the animation on any event in any control that I want to call, but only in XAML. I don't want to use code behind
Thanks
May related link:
WPF/XAML: multiple controls using the same events - is there an easier way?
https://social.msdn.microsoft.com/Forums/vstudio/en-US/48b22609-9055-4d3b-8f26-b87305e2dc8e/how-to-handle-child-event-in-parent-control?forum=wpf
start wpf animation from other element
https://www.codeproject.com/Tips/227733/Passing-events-from-child-to-parent-controls-built
.
Update
Full XAML Code:
<Window x:Class="WpfApp11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp11"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="Atash" TargetType="FrameworkElement">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderThickness)">
<EasingThicknessKeyFrame KeyTime="0:0:0.2" Value="1,1,10,1">
<EasingThicknessKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingThicknessKeyFrame.EasingFunction>
</EasingThicknessKeyFrame>
</ThicknessAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0:0:0.2" Value="#FF23A232">
<EasingColorKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard x:Name="DoOnOut">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderThickness)">
<SplineThicknessKeyFrame KeyTime="0" Value="1,1,10,1"/>
<EasingThicknessKeyFrame KeyTime="0:0:0.2" Value="1">
<EasingThicknessKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingThicknessKeyFrame.EasingFunction>
</EasingThicknessKeyFrame>
</ThicknessAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="0" Value="#FF23A232"/>
<EasingColorKeyFrame KeyTime="0:0:0.2" Value="White">
<EasingColorKeyFrame.EasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Background="#FF1B1B1C">
<Grid x:Name="GrdBtn2" Style="{StaticResource Atash}" Margin="323,167,345,135">
<Border x:Name="border2" Visibility="Visible" BorderBrush="White" BorderThickness="5,3" CornerRadius="80" Cursor="Hand" Width="auto" Height="auto" Background="#FF4CAF50">
<Border.Effect>
<DropShadowEffect Color="White" BlurRadius="12" ShadowDepth="0"/>
</Border.Effect>
</Border>
<TextBlock Text=" Automasion" LineStackingStrategy="MaxHeight" LineHeight="20" TextAlignment="Center" Width="auto" Height="auto" TextWrapping="Wrap" Margin="25,22,27,21" Foreground="White" Background="{x:Null}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
Error On MouseEnter of Grid:
System.InvalidOperationException: ''BorderBrush' property does not point to a DependencyObject in path '(0).(1)'.'
If you set the TargetType of the Style to FrameworkElement, you can apply it to any FrameworkElement including a Grid and a Border:
<Style x:Key="Atash" TargetType="FrameworkElement">
...
Obviously you cannot animate the BorderThickness of anything else than a Border though.
Your animation also assumes that the Background of the element to be animated is set to a SolidColorBrush and that it has a DropShadowEffect applied.
I have a customized animated button with this style:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Resources>
<Storyboard x:Key="MenuBtnMouseEnterSb">
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="btnScaleTransform"
To="0.92"
Duration="0:0:0.1">
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="2" EasingMode="EaseInOut"></ExponentialEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="btnScaleTransform"
To="0.92"
Duration="0:0:0.1">
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="2" EasingMode="EaseInOut"></ExponentialEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<Storyboard x:Key="MenuBtnMouseExitSb">
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="btnScaleTransform"
To="1"
Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="1" Bounciness="5"></BounceEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="btnScaleTransform"
To="1"
Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="1" Bounciness="5" EasingMode="EaseInOut">
</BounceEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<Storyboard x:Key="MenuBtnMouseClickSb" AutoReverse="True">
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="btnScaleTransform"
To="0.75"
Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="btnScaleTransform"
To="0.75"
Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</ControlTemplate.Resources>
<Border x:Name="border" BorderBrush="Transparent" CornerRadius="10" Background="{TemplateBinding Background}" Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}" TextBlock.Foreground="{TemplateBinding Foreground}" Margin="{TemplateBinding Margin}" Padding="{TemplateBinding Padding}" RenderTransformOrigin="0.5, 0.5">
<ContentPresenter Margin="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" x:Name="btnScaleTransform"></ScaleTransform>
</TransformGroup>
</Border.RenderTransform>
<Border.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard Storyboard="{DynamicResource MenuBtnMouseEnterSb}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<BeginStoryboard Storyboard="{DynamicResource MenuBtnMouseExitSb}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.PreviewMouseDown">
<BeginStoryboard Storyboard="{DynamicResource MenuBtnMouseClickSb}" />
</EventTrigger>
</Border.Triggers>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Basically, whenever mouse enters this button - button becomes smaller and when PreviewMouseDown event happens (when I press mouse button) it must become even more small. But what actually happens is that button returns to it's original size when I press a left button - i.e. MouseLeave event is fired and when I release left mouse button MouseEnter is fired again.
I added handlers for PreviewMouseDown and MouseLeave events and after PreviewMouseDown is handled - MouseLeave is handled immediately which I suppose is causing the MouseExit animation to run instead of MouseClick.
I've tested it again with Console.Writelines in each handler and it seems that MouseLeave handler is not called but animation still works as I described earlier.
I removed MouseLeave eventtrigger and now proper mousedown animation is running but it returns only to 0.92 scale (MouseEnter animation) and cannot return to original size when cursor is outside (that's what MouseLeave animation did).
The solution for my problem is simple: Use VisualStates instead of Triggers for such task.
I just copied my animations into VisualStates and they now work as I expected:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="btnScaleTransform"
To="1"
Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="1" Bounciness="5"></BounceEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="btnScaleTransform"
To="1"
Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="1" Bounciness="5" EasingMode="EaseInOut">
</BounceEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="btnScaleTransform"
To="0.92"
Duration="0:0:0.1">
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="2" EasingMode="EaseInOut"></ExponentialEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="btnScaleTransform"
To="0.92"
Duration="0:0:0.1">
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="2" EasingMode="EaseInOut"></ExponentialEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="btnScaleTransform"
To="0.75"
Duration="0:0:0.2">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="btnScaleTransform"
To="0.75"
Duration="0:0:0.2">
</DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Although it is kind of weird how Trigger based animations work...
I want to use a DoubleAnimation multiple times. Here the code:
<Window x:Class="Project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<DoubleAnimation x:Key="LeftAnimation" Storyboard.TargetProperty="(Canvas.Left)" From="0" To="100" />
<DoubleAnimation x:Key="TopAnimation" Storyboard.TargetProperty="(Canvas.Top)" From="0" To="100" />
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="AnimatedBorder1">
<StaticResourceExtension ResourceKey="LeftAnimation" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard Storyboard.TargetName="AnimatedBorder2">
<StaticResourceExtension ResourceKey="LeftAnimation" />
<StaticResourceExtension ResourceKey="TopAnimation" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Canvas>
<Border x:Name="AnimatedBorder1" Background="Blue" Width="20" Height="20" />
<Border x:Name="AnimatedBorder2" Background="Red" Width="20" Height="20" />
</Canvas>
</Window>
The code works fine, but ReSharper underlines LeftAnimation in line <StaticResourceExtension ResourceKey="LeftAnimation" /> and says: "Invalid resource type: expected type is 'TriggerCollection', actual type is 'DoubleAnimation'."
Is that really a problem or is this a bug in ReSharper (version 9.2)?
Here is an editted solution:
Resources:
<Window.Resources>
<TimelineCollection x:Key="TimelinesCollectionKey1">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="0" To="100" />
</TimelineCollection>
<TimelineCollection x:Key="TimelinesCollectionKey2">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" From="0" To="100" />
</TimelineCollection></Window.Resources>
Triggers:
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="AnimatedBorder1">
<!--<StaticResourceExtension ResourceKey="LeftAnimation" />-->
<Storyboard.Children>
<ParallelTimeline Children="{StaticResource TimelinesCollectionKey1}"></ParallelTimeline>
</Storyboard.Children>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard Storyboard.TargetName="AnimatedBorder2">
<Storyboard.Children>
<ParallelTimeline Children="{StaticResource TimelinesCollectionKey1}"></ParallelTimeline>
<ParallelTimeline Children="{StaticResource TimelinesCollectionKey2}"></ParallelTimeline>
</Storyboard.Children>
</Storyboard>
</BeginStoryboard>
</EventTrigger></Window.Triggers>
Targets:
<Canvas>
<Border x:Name="AnimatedBorder1" Background="Blue" Width="20" Height="20" />
<Border x:Name="AnimatedBorder2" Background="Red" Width="20" Height="20" /></Canvas>
Regards,
I have a user control in WPF. I have a button control within the control. I want the control to fade in and out when the mouse enters and leaves the control. The problem is when the mouse enters the button and leaves the control, it fades. Below is the code.
<UserControl x:Class="WpfPresentationEditor.PresentationWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="68" d:DesignWidth="793">
<UserControl.Resources>
<Storyboard x:Key="onMouseEnter">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="LayoutRoot">
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseIn"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="onMouseLeave">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="LayoutRoot">
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard x:Name="onMouseEnter_BeginStoryBoard" Storyboard="{StaticResource onMouseEnter}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard x:Name="onMouseLeave_BeginStoryBoard" Storyboard="{StaticResource onMouseLeave}"/>
</EventTrigger>
<EventTrigger RoutedEvent="UserControl.Loaded">
<BeginStoryboard Storyboard="{StaticResource onMouseLeave}" />
</EventTrigger>
</UserControl.Triggers>
<Grid x:Name="LayoutRoot" Opacity=".5">
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"></Canvas>
<Button Click="btnClose_Click" Height="44" HorizontalAlignment="Left" Name="btnClose" VerticalAlignment="Center" Width="44" Margin="12,12,0,12">
<Button.Background>
<ImageBrush ImageSource="images/exit.png" />
</Button.Background>
</Button>
</Grid>
This is what I ended up doing to make this work as desired.
I changed the RoutedEvent to UserControl.MouseEnter rather than Mouse.MouseEnter.
Then I created my own user control and created an Image button like this http://blogs.msdn.com/b/knom/archive/2007/10/31/wpf-control-development-3-ways-to-build-an-imagebutton.aspx.
So my altered code now looks like this....
<UserControl x:Class="WpfPresentationEditor.PresentationWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="68" d:DesignWidth="793" xmlns:my="clr-namespace:WpfPresentationEditor">
<UserControl.Resources>
<ImageBrush x:Key="exitImage" ImageSource="images/exit.png"/>
<!--These are the story boards for the control-->
<Storyboard x:Key="onMouseEnter">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="LayoutRoot">
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseIn"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="onMouseLeave">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="LayoutRoot">
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="UserControl.MouseEnter">
<BeginStoryboard x:Name="onMouseEnter_BeginStoryBoard" Storyboard="{StaticResource onMouseEnter}"/>
</EventTrigger>
<EventTrigger RoutedEvent="UserControl.MouseLeave">
<BeginStoryboard x:Name="onMouseLeave_BeginStoryBoard" Storyboard="{StaticResource onMouseLeave}"/>
</EventTrigger>
<EventTrigger RoutedEvent="UserControl.Loaded">
<BeginStoryboard Storyboard="{StaticResource onMouseLeave}" />
</EventTrigger>
</UserControl.Triggers>
<StackPanel x:Name="LayoutRoot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal" Background="Black">
<my:ImageButtonControl ButtonBase.Click="btnClose_Click" x:Name="imageButtonControl1" Width="44" Image="images/exit.png" Height="44"/>
</StackPanel>
I would make 2 controls, one containing everything but your button and another one with just the button.
Then put them both in a grid so they will stack upon each other, and voila.
I have the following xaml that illustrates a circle that grows and shrinks in size. I want my center point of my circle to stay put on my canvas as the animation runs.
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" From="10" To="360" Duration="0:0:3" Storyboard.TargetName="GrowMe" Storyboard.TargetProperty="Width"/>
<DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" From="10" To="360" Duration="0:0:3" Storyboard.TargetName="GrowMe" Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
<Canvas Width="640" Height="480">
<Ellipse Width="10" Height="10" Stroke="Red" Canvas.Left="80" Canvas.Top="31"/>
<Ellipse x:Name="GrowMe" Width="10" Height="10" Stroke="Cyan" Canvas.Left="205" Canvas.Top="203"/>
Here is an example of a button that does exactly this. Note the RenderTransformOrigin property.
<Button RenderTransformOrigin="0.5,0.5" Command="{Binding ClickCommand}" Cursor="Hand">
<Button.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1" />
</Button.RenderTransform>
<Button.Template>
<ControlTemplate>
<Label>
<Label.Background>
<VisualBrush Visual="{Binding Path=Shape, Converter={StaticResource myTestConv}}" />
</Label.Background>
</Label>
</ControlTemplate>
</Button.Template>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX"
To="1.3" Duration="0:0:0.1" AccelerationRatio="0.5"/>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY"
To="1.3" Duration="0:0:0.1" AccelerationRatio="0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX"
To="1" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY"
To="1" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<local:TooltipServiceEx.ToolTipEx>
<local:ToolTipEx
HideToolTipTimeout="{StaticResource TooltipTimeout}" />
</local:TooltipServiceEx.ToolTipEx>
<ToolTipService.ToolTip>
<ToolTip
Template="{StaticResource EventTooltipStyle}" />
</ToolTipService.ToolTip>
</Button>
You can do it easily animating ScaleTransform inside RenderTransform and set RenderTransformOrigin = "0.5, 0.5".
--EDIT--
Then your change your storyboard to following:
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" From="10" To="180" Duration="0:0:3" Storyboard.TargetName="GrowMe" Storyboard.TargetProperty="Width"/>
<DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" From="10" To="180" Duration="0:0:3" Storyboard.TargetName="GrowMe" Storyboard.TargetProperty="Height"/>
<DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" By="-180" Duration="0:0:3" Storyboard.TargetName="GrowMe" Storyboard.TargetProperty="(Canvas.Left)"/>
<DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" By="-180" Duration="0:0:3" Storyboard.TargetName="GrowMe" Storyboard.TargetProperty="(Canvas.Top)"/>
</Storyboard>
You need to devide your growth into half and add it to Width/Height and subtract it from Left/Top.