I love the whole concept of Material Design, and especially its animations! And on my phones Musicplayer when pressing the Play/Pause button a relly neat animation starts blending between the two icons like this:
How can you do something like this in WPF? Maybe with Paths?
Tips would be really appreciated!! thanks
I have done something similar in Material Design in XAML Toolkit which replicates the menu 'burger' transitioning into a back arrow, using paths.
Essentially the menu is three horizontal line paths. To convert to a back arrow the two outer (above/below lines) are angled, scaled, and slightly moved using DoubleAnimationUsingKeyFrames.
On top of that the entire canvas is rotated 180 degrees.
This was made a lot easier by doing in blend.
The full XAML file is on GitHub here, you are looking for the MaterialDesignHamburgerToggleButton style. (I tried posting a large snippet, but Stack Overflow errored.)
<Window x:Class="Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="650" Width="500"
xmlns:local="clr-namespace:Demo">
<Grid>
<Button Height="40" HorizontalAlignment="Left" IsEnabled="True" IsHitTestVisible="True" Margin="262,219,0,0" Name="home_btn" VerticalAlignment="Top" Width="89">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image Name="Normal" Source="pause.png" />
<Image Name="Hover" Source="play.png" Opacity="0"/>
<Image Name="Pressed" Source="pause.png" Opacity="0" />
</Grid>
<ControlTemplate.Resources>
<Storyboard x:Key="MouseDownTimeLine">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Pressed" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="00:00:00.05" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="MouseUpTimeLine">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Pressed" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="00:00:00.25" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="MouseEnterTimeLine">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Hover" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="00:00:00.25" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="MouseExitTimeLine">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Hover" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="00:00:00.25" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MouseDownTimeLine}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MouseUpTimeLine}"/>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MouseEnterTimeLine}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MouseExitTimeLine}"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Window>
Related
SliderMenu in Youtube
I can't do something similar to this video, the animation doesn't stop and the menu always flashes. Here's my XAML:
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin"
From="-70,0,0,0" To="15,0,15,0"
DecelerationRatio=".8" Duration="0:0:0.7"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin"
From="15,0,15,0" To="-70,0,0,0"
DecelerationRatio=".8" Duration="0:0:0.7"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
What's the problem with it?
The flickering is happening because as soon as the StackPanel starts animating, it moves itself out of the way (of the mouse cursor), causing the other animation to start. I don't see you full code, so here are a couple of pointers to improve your code.
Make sure the entire menu is hittest-visible (e.g. by setting a background)
Don't use two triggers, use one trigger with enter and exit-triggers
Avoid the margin on the left - it can cause the same kind of flickering again.
<StackPanel Background="Blue" Width="100" HorizontalAlignment="Left">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Margin" Value="-70,0,0,0"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin"
From="-70,0,0,0" To="0,0,0,0"
DecelerationRatio=".8" Duration="0:0:0.7"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin"
From="0,0,0,0" To="-70,0,0,0"
DecelerationRatio=".8" Duration="0:0:0.7"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Button Width="100" Height="25"></Button>
</StackPanel>
Result:
My UI components are taking a while to load, I've tried as much as I can to reduce the loading time (and will probably give a few more things a go in future), but I've also introduced a 'busy' indicator to give the user some feedback.
However, I've found the animation stops when components are being loaded, making it pretty useless. I thought WPF had animation on a separate thread?
Here's what I mean, the loading indicator is behind the 'page' being loaded and unlaoded, so it only shows when one is removed. To demonstrate, I've added one that can be seen all the time to the side:
Is there anything I can do to run the storyboard animation in another thread?
It's part of the style, taken from the Material Design XAML toolkit (I've tried to pull out relevant info:
<Style x:Key="MaterialDesignLinearProgressBar" TargetType="{x:Type ProgressBar}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
...
<Grid x:Name="TemplateRoot" RenderTransformOrigin="0,0.5" Opacity="0" Height="4">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0" ScaleY="0" />
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
...
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Animation">
<EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="Animation">
<EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
</PointAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
...
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="transitions:TransitionAssist.DisableTransitions" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource OnLoadedNoAnimation}" Name="BeginStoryboardOnLoadedNoAnimation" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BeginStoryboardOnLoadedNoAnimation" />
</Trigger.ExitActions>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsVisible" Value="True" />
<Condition Property="transitions:TransitionAssist.DisableTransitions" Value="False" />
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Full code https://github.com/ButchersBoy/MaterialDesignInXamlToolkit/blob/master/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ProgressBar.xaml
I need some help from WPF blend wizards out there. This is more for knowledge sake as I can already implement a datatrigger in a style through explicitly changing xaml, but I would like to know if there is a way to do this through Blend.
Lets start with some code!
<Image x:Name="image" HorizontalAlignment="Center" Height="299" Margin="24,27,24,0" VerticalAlignment="Top" Width="832" RenderTransformOrigin="0.5,0.5" Source="img/SpiderLogo.png" d:IsHidden="True" Grid.RowSpan="2">
<Image.Style>
<Style>
<Style.Resources>
<Storyboard x:Key="LoadTitleScreen">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="0:0:0.6" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoginValid, Source={StaticResource viewModel}}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource LoadTitleScreen}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
All is well and working here! Now after building another image and editing its style resource by:
1) right-clicking the image in "Objects and Timeline"
2) edit style
3) creating a style resource
I can create storyboards and the whole nine but cannot add an datatrigger to it through Blend's interface. I go to Assets Menu to drag a ChangePropertyAction onto the style but the entire Assets Menu is greyed out/non-functional.
<Image x:Name="titleBackgroundImage" HorizontalAlignment="Center" Height="610" VerticalAlignment="Center" Width="899" Source="img/RPVBackground.jpg" Stretch="Fill" Margin="-9,-10,-10,-50" Grid.RowSpan="2" Visibility="Collapsed" Style="{DynamicResource TitleScreenBackgroundImage}">
<Image.Resources>
<Style x:Key="TitleScreenBackgroundImage" TargetType="{x:Type Image}">
<Style.Resources>
<Storyboard x:Key="LoadTitleScreenBackground">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="{x:Null}">
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="{x:Null}">
<DiscreteObjectKeyFrame KeyTime="0:0:0.6" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="0:0:1.1" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Style.Resources>
</Style>
</Image.Resources>
</Image>
This is what I'm stuck with, a nice storyboard created in the style resource but the inability to add a trigger to call it =(
Please help if you know how! Many thanks!
If it's just Triggers you want and the that window isn't immediately open by default you can hit;
Ctrl + ALT + R
...or select from the top menu View -> Triggers Window and manage your Triggers there.
As to why your Assets tab is grayed out, well that could be a few different reasons. Would need to know more to diagnose that one. First guess would be you're just missing SDK or something? Hope this helps.
I'm trying to animate a canvas in WPF using Storyboard and DoubleAnimationsUsingKeyFrames my code goes something like this:
<Canvas x:Name="bgCanvas" Height="261" Canvas.Top="-262" Width="720">
<Canvas.Background>
<ImageBrush ImageSource="Resources/backgroundBlurred.png" Stretch="UniformToFill"/>
</Canvas.Background>
<Canvas.Resources>
<Storyboard x:Key="bgAnim" x:Name="bgAnim">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(bgCanvas.Opacity)" Storyboard.Target="bgCanvas">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="1.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Canvas.Resources>
I'm also using Mahapps.Metro for the project. <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(bgCanvas.Opacity)" Storyboard.Target="bgCanvas"> is underlined by blue lines and the error reads:
bgCanvas is not supported in a Windows Presentation Format (WPF) Application.
I'm not sure whats wrong with the code.
BONUS: Is this the right way to animate a canvas in WPF?
Sorry for the nub questions.
You could start the animation in an EventTrigger on the Loaded event of the Canvas:
<Canvas ...>
<Canvas.Background>
<ImageBrush .../>
</Canvas.Background>
<Canvas.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
Replace (bgCanvas.Opacity) with (Canvas.Opacity)
How to change respectively between two storyboards within the same ex. MouseDown event or button Click event targeting same property ex. visibility.
1st click -> Visibility from 1 to 0 ( Fade Out )
2nd click -> Visibility from 0 to 1 ( Fade In )
3rd click -> Visibility from 1 to 0 ( Fade Out )
4th click -> Visibility from 0 to 1 ( Fade In )
and so on ...
Fade In
<Style TargetType="{x:Type FrameworkElement}" x:Key="FadeIn">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Fade Out
<Style TargetType="{x:Type FrameworkElement}" x:Key="FadeOut">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Thanks in advance,
I think a toggle button is the appropriate control for this, but if you need to use a normal button for whatever reason, use two of them instead of one:
<Window
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:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" mc:Ignorable="d"
x:Class="TestBed.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Storyboard x:Key="Hide_Border">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Btn_Show">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Btn_Hide">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Show_Border">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Btn_Show">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Btn_Hide">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="Btn_Hide">
<BeginStoryboard Storyboard="{StaticResource Hide_Border}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="Btn_Show">
<BeginStoryboard x:Name="Show_Border_BeginStoryboard" Storyboard="{StaticResource Show_Border}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.8*"/>
</Grid.ColumnDefinitions>
<Button x:Name="Btn_Hide" Content="Hide" VerticalAlignment="Top"/>
<Button x:Name="Btn_Show" Content="Show" VerticalAlignment="Top" Visibility="Collapsed"/>
<Border x:Name="border" Grid.Column="1" Background="Red" />
</Grid>