How to make Hamburger Menu Transition Animation in XAML only - c#

I want to create the Hamburger Menu Transition Animation in XAML only.
I only found tutorials and examples of how to do this with code-behind.
But this is something I want to avoid at all costs.
This is how the animation looks like:
Animation (no direct GIF possible at the moment)
One example I found
XAML:
<Window.Resources>
<Storyboard x:Key="OpenMenu">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="rectangle">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="43.5"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="rectangle1">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-43.5"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="rectangle2">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="GridMenu">
<EasingDoubleKeyFrame KeyTime="0" Value="70"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="200"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="CloseMenu">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="rectangle">
<EasingDoubleKeyFrame KeyTime="0" Value="43.5"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="rectangle1">
<EasingDoubleKeyFrame KeyTime="0" Value="-43.5"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="rectangle2">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="GridMenu">
<EasingDoubleKeyFrame KeyTime="0" Value="200"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="70"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
code-behind:
private void ButtonMenu_Click(object sender, RoutedEventArgs e)
{
if(StateClosed)
{
Storyboard sb = this.FindResource("OpenMenu") as Storyboard;
sb.Begin();
}
else
{
Storyboard sb = this.FindResource("CloseMenu") as Storyboard;
sb.Begin();
}
StateClosed = !StateClosed;
}
Is there anyway to achive the same result using XAML-only.
I dont want any code-behind for this animation...
I know how to create each Storyboard for the animation itself.
But the same button has to execute two different Storyboards,
depending on the state of the menu?

Here is an example using a ToggleButton instead. Since the ToggleButton has built in Checked / Unchecked events, you can use those in EventTriggers to run your storyboards.
<ToggleButton ... >
<!-- Your menu icon XAML/rectangles go here -->
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Checked">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource OpenMenu}" />
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource CloseMenu}" />
</EventTrigger.Actions>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>

Related

How to reset control after ScaleTransform animation

I am working on a Windows 10 universal application targeting PC. I want to run an animation repeatedly (not in a "RepeatBehaviour" loop, but run it again after something else happens), but I am struggling with resetting the animated control AFTER animation completes.
One of the animations in a storyboard is TranslateTransform. After it completes, I can re-position the control using Canvas.SetLeft and Canvas.SetTop in code. However, after I "disappear" a control using ScaleTransform animation the control remains invisible and I cannot make it visible again. Changing control Height and Width does nothing.
Using AutoReverse in XAML does resolve the issue, but I do not want this to be visible to the user. The control is supposed to "disappear". I want to reset the control in code before the next run, behind the scenes.
My Storyboard and the Border control which I am animating are shown below. This works fine the first time. The image flies across the screen with some rotation and then disappears. Second time the image is invisible until the scale transform runs again.
I tried adding another ScaleTransform animation before the first key frame, to scale from 0 to 1 quickly (I tried duration of "0" or "0.1") but that breaks the whole thing for some reason.
I wonder if RenderTransformOrigin has something to do with this, but am unsure how to use it with TransformGroup. So it is set to (0.5,0.5) to center the rotation.
Any help would be appreciated.
<Storyboard x:Name="myStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(Canvas.Top)"
EnableDependentAnimation="False">
<SplineDoubleKeyFrame x:Name="MyTopSpline" KeyTime="0:0:1.9"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(Canvas.Left)"
EnableDependentAnimation="False"">
<SplineDoubleKeyFrame x:Name="MyLeftSpline" KeyTime="0:0:1.9"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"
From="0" To="360" BeginTime="0:0:1.0" Duration="0:0:1.9" />
<DoubleAnimation Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)"
From="1" To="0" BeginTime="0:0:1.9" Duration="0:0:0.9" />
<DoubleAnimation Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)"
From="1" To="0" BeginTime="0:0:1.9" Duration="0:0:0.9" />
</Storyboard>
<Border Name="myImage" Height="244" Width="244" Canvas.Left="200" Canvas.Top="100" Visibility="Collapsed"
RenderTransformOrigin=".5,.5">
<Border.RenderTransform>
<TransformGroup>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup>
</Border.RenderTransform>
</Border>
First, let's clean up your code a bit. Instead of using a TransformGroup, you should use a composite transform object called CompositeTransform as it's a lot easier to work with. Don't forget to name it too.
<Border.RenderTransform>
<CompositeTransform x:Name="myImageTransform" />
</Border.RenderTransform>
So now your Storyboard should look like the following. Note that I have added a myStoryboard_Completed handler to it.
<Storyboard x:Name="myStoryboard"
Completed="myStoryboard_Completed">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(Canvas.Top)">
<SplineDoubleKeyFrame x:Name="MyTopSpline" KeyTime="0:0:1.9" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(Canvas.Left)">
<SplineDoubleKeyFrame x:Name="MyLeftSpline" KeyTime="0:0:1.9" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)"
From="0"
To="360"
BeginTime="0:0:1.0"
Duration="0:0:1.9" />
<DoubleAnimation Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)"
From="1"
To="0"
BeginTime="0:0:1.9"
Duration="0:0:0.9" />
<DoubleAnimation Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)"
From="1"
To="0"
BeginTime="0:0:1.9"
Duration="0:0:0.9" />
</Storyboard>
Inside this handler, all you need to do is to reset all the transform attributes and canvas offsets.
private void myStoryboard_Completed(object sender, object e)
{
myImage.Visibility = Visibility.Collapsed;
Canvas.SetTop(myImage, 100);
Canvas.SetLeft(myImage, 200);
myImageTransform.Rotation = 0;
myImageTransform.ScaleX = myImageTransform.ScaleY = 1;
}
But here is a better way.
Instead of doing some of the stuff in code behind, why not wrap all the logic inside the same Storyboard? All you need is to use DoubleAnimationUsingKeyFrames to define both start and end values for each of your animations.
<Storyboard x:Name="myStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(Canvas.Top)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="100" />
<SplineDoubleKeyFrame x:Name="MyTopSpline" KeyTime="0:0:1.9" Value="100" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="myImage"
Storyboard.TargetProperty="(Canvas.Left)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="200" />
<SplineDoubleKeyFrame x:Name="MyLeftSpline" KeyTime="0:0:1.9" Value="200" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)"
Storyboard.TargetName="myImage">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:2.9" Value="360" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)"
Storyboard.TargetName="myImage">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:1.9" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:2.8" Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)"
Storyboard.TargetName="myImage">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:1.9" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:2.8" Value="0" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="myImage">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:3">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
Hope this helps!
Adding this line of code to StoryBoard completed event resolved the issue:
MyStoryboard.Stop();

How to have a storyboard use a border in a calendar control template

I have edited the CalendarItem Template of the Calendar control. I have added my own border to this template.
I am implementing the Calendar with my CalendarItemStyle.
I normally have a StoryBoard like this
<Storyboard x:Key="sbBorderHighlighted">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
and i call it like this
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard x:Name="sbBorderHighlighted_BeginStoryboard" Storyboard="{StaticResource sbBorderHighlighted}"/>
</EventTrigger>
So if this event trigger is used on the Calendar Control how do I have to change the StoryBoard so it targets the border in my template?

my storyboard skips some keyframes

I would like to swing an ellipse to one place and then shrink it. After the first storyboard stops, I intend to stretch the ellipse with the ellipse. However the shrinking process doesn't work at all. Hard to firgure out why~ Any help or tips will be appreciated. Thanks~
<Window.Resources>
<Storyboard x:Key="Swing" Duration="0:0:7">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="Leg" AutoReverse="True" RepeatBehavior="Forever">
<EasingDoubleKeyFrame KeyTime="0" Value="27.081"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="-25.783"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="Leg">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:6" Value="489.676"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="Leg">
<EasingDoubleKeyFrame KeyTime="0:0:6" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:7" Value="0.2"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Swing2" BeginTime="0:0:7">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="Leg">
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="0.2"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="Swing_BeginStoryboard" Storyboard="{StaticResource Swing}"/>
<BeginStoryboard x:Name="Swing_BeginStoryboard1" Storyboard="{StaticResource Swing2}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Ellipse x:Name="Leg" Fill="#FFF4F4F5" HorizontalAlignment="Left" Margin="156,204,0,147" Stroke="Black" Width="33" RenderTransformOrigin="0.464,-0.014">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="27.081"/>
<TranslateTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
If I change the second storyboard as below, the ellipse will be shinking from the beginning.
<Storyboard x:Key="Swing2">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="Leg">
<EasingDoubleKeyFrame KeyTime="0:0:7" Value="0.2"/>
<EasingDoubleKeyFrame KeyTime="0:0:9" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>

InvalidOperationException was unhandled - WPF Path

Here is my UserControl
<Grid x:Name="Grid">
<Path Name="Path1" Data="M0.5,0.5 L179.5,0.5 L179.5,249.5 L0.5,249.5 z" Stretch="Fill" Stroke="Purple" StrokeThickness="1">
<Path.Fill>
<SolidColorBrush Color="Blue" Opacity="0.4"/>
</Path.Fill>
</Path>
<Grid.Resources>
<Storyboard x:Key="Path_Ani">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="Path1">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-58.515"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="Path1">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-129.167"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="Path1">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.544"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Path1">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.806"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(SkewTransform.AngleX)" Storyboard.TargetName="Path1">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="5.628"/>
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetProperty="(Path.Data).(PathGeometry.Figures)[0].(PathFigure.Segments)[1].(LineSegment.Point)" Storyboard.TargetName="Path1">
<EasingPointKeyFrame KeyTime="0" Value="100.5,140.5"/>
<EasingPointKeyFrame KeyTime="0:0:0.2" Value="104.175476605516,140.5"/>
</PointAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
</Grid>
And i call the StoryBoard like that:
private void MyTest_MouseDown(object sender, MouseButtonEventArgs e)
{
Storyboard sbdCardAnim = (Storyboard)MyTest.Grid.Resources["Path_Ani"];
sbdCardAnim.Begin();
}
And i get this error when i click:
'[Unknown]' property does not point to a DependencyObject in path '(0).(1)[3].(2)'.
How do I solve the problem that is causing this error?
Your animation expects the path to have a bunch of transformations defined as the RenderTransform, however your path's actual RenderTransform seems to be null (as it is not defined).
<!-- The RenderTransform which is expected looks something like this
(while no animation targets the third and therefore unknown transform
it only makes sense for it to be a RotateTransform) -->
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Path.RenderTransform>
The path in the PointAnimation also does not seem to fit the actual data in the path as there will still be an error if the RenderTransform is defined as expected.

Problem with conditional trigger (or animation)

I have this code:
<UserControl.Resources>
<Storyboard x:Key="OnMouseEnterToDocumentsMenu">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Documents">
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.8"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnMouseLeaveFromDocumentsMenu">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Documents">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0.3"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnMouseEnterToPeopleMenu">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="People">
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.8"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnMouseLeaveFromPeopleMenu">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="People">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0.3"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="Documents">
<BeginStoryboard Storyboard="{StaticResource OnMouseEnterToDocumentsMenu}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="Documents">
<BeginStoryboard Storyboard="{StaticResource OnMouseLeaveFromDocumentsMenu}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="People">
<BeginStoryboard Storyboard="{StaticResource OnMouseEnterToPeopleMenu}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="People">
<BeginStoryboard Storyboard="{StaticResource OnMouseLeaveFromPeopleMenu}"/>
</EventTrigger>
</EventTrigger>
</UserControl.Triggers>
Purpose
I'm animation a Menu. Menu elements are TextBlocks, I'm simulating a "Hover fading".
Problem
The code I posted animates the opacity once the mouse enter or leave. I have another method (code behind) that sets to "1" the opacity of the menu element once the user clicks it on the menu (and load the appropriate user control on its respective container). I guess this method is working, BUT once the MouseLeave animation start it fades the opacity again no matter if that element was selected or not (If its opacity was 1 not 0.8).
Needs
Is there a way to specify that the MouseLeave trigger fires only when the Source Opacity is not 1. Or to execute the storyboard only if the TargetProperty is not 1.
Thanks.
PS
Is there a way to prevent code duplication given that the animation storyboards are the same (for focus and for fade) and that more menus will come. I duplicate it because I need to put the TargetName and is different but I suppose there is another "cleaner" way.
This behavior is due to dependency property value precedence. I haven't found a xamlish way to do this. But with code-behind here is how:
Subscribe to the storyboard's completed event.
Look for the menuItems that has been selected and call BeginAnimation on it.
You can find more infomation on this here.
private void Storyboard_Completed(object sender, EventArgs e)
{
foreach (var menuItem in this.file.Items)
{
var item = (MenuItem)menuItem;
if (item.IsChecked)
{
item.BeginAnimation(UIElement.OpacityProperty, null);
item.Opacity = 1;
}
}
}
I have looked for IsChecked to say if a menuItem is selected. Use your own methodology if this doesnt suit you.
For question 2 add this to resources and you can get rid of all individual triggers and duplicate storyboards:
<Window.Resources>
<Storyboard x:Key="HoverOn">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.8"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="HoverOff" Completed="Storyboard_Completed">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0.3"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Style TargetType="{x:Type MenuItem}">
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter" >
<BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource HoverOff}"/>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>

Categories