Trigger on MouseHover? - wpf

I was wondering if there was a way to setup a trigger on MouseHover, not MouseOver.
I would like an action to occur when the user keeps their mouse over a specified area for X seconds instead of when they just MouseOver it in passing.

To make the IsMouseOver trigger begin after X seconds, you should be able to use a Storybard and set the BeginTime Property. Here's an example for a Button which increases its size by 50% when the mouse is hovering it for 2 seconds.
To skip the animation part you can set Duration="0" for the DoubleAnimations
<Button Content="Button"
Height="23"
Width="75"
RenderTransformOrigin="0.5 0.5">
<Button.RenderTransform>
<ScaleTransform />
</Button.RenderTransform>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard BeginTime="00:00:02">
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleX)" To="1.5"/>
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleY)" To="1.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleX)" To="1.0"/>
<DoubleAnimation Storyboard.TargetProperty="(Button.RenderTransform).(ScaleTransform.ScaleY)" To="1.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Related

Can this animation not spam?

I am pretty new to xaml and wpf in general, so excuse me if the solution is easy or the xaml i used is bad. I am unsure if this is possible, but if there is some kind of solution please let me know!
Here is a video of what i am trying to fix:
https://imgur.com/a/NmnV50S
If the video doesn't explain my problem, here it is: can the button animation not spam or bug when the user moves his cursor very fast across the button?
Here is the xaml for the animation:
<Style x:Key="SlidingButtonToRight" TargetType="Button">
<Setter Property="Width" Value="270"/>
<Setter Property="Height" Value="80"/>
<Setter Property="UseLayoutRounding" Value="True"/>
<Setter Property="ClipToBounds" Value="True"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Button.RenderTransform">
<Setter.Value>
<TranslateTransform/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border" BorderThickness="0" BorderBrush="Black" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.8" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)" From="0" To="110" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)" From="110" To="0" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
<Style x:Key="SlidingButtonToLeft" TargetType="Button" BasedOn="{StaticResource SlidingButtonToRight}">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)" From="0" To="-110" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)" From="-110" To="0" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
Here is the xaml for the button on which i use the animation style:
<Button x:Name="button4" Click="Button4_Click" Style="{DynamicResource SlidingButtonToLeft}" Margin="0,50,-186,0" VerticalAlignment="Top" HorizontalAlignment="Right">
<Button.Background>
<ImageBrush ImageSource="Assets/programm-bt.png"/>
</Button.Background>
<TextBlock Text="Programm" TextAlignment="Left" Width="105" Margin="0,0,-25,0" HorizontalAlignment="Center"/>
</Button>
To reduce the animation spam, you can set a BeginTime property on your MouseLeave animation to give the user enough time to move the mouse off the button before the animation starts.
You can start with .2 seconds and tweak from there:
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0.2"
Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)"
From="-110"
To="0"
Duration="0:0:0.3" />
</Storyboard>
I figured it out, by removing From ,the animation doesn't start from 0 every time the user hovers over the button. Here is a video from before and after the change.
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)"
To="110"
Duration="0:0:0.2"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Image.RenderTransform).(TranslateTransform.X)"
To="0"
Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>

How to write a style for a control that changes width of another control?

Is there any way to write a Style for a control that changes width of another control?
<Style x:Key="SubMenuStyle" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="LightCyan"/>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Menu" Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
This code leads to error:
TargetName property cannot be set on a Style Setter
I know I can write codes below and it works:
<Label Name="Owners" Margin="0,1,0,0" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}">
<Label.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Menu" Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Label.Triggers>
</Label>
But since I use this trigger in multiple labels I want to write it in a style once.
This is my code to define controls:
<Border Name="Menu" Grid.Column="2" Grid.Row="1" Width="0" HorizontalAlignment="Right" BorderBrush="LightBlue" BorderThickness="2" CornerRadius="2" Background="LightCyan">
<StackPanel Name="MenuPanel">
<Button Style="{StaticResource MenuButtonsStyle}">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ListsMenu" Storyboard.TargetProperty="Height" To="86" Duration="0:0:.6"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<StackPanel Name="ListsMenu" Height="0">
<Label Name="Owners" Margin="0,1,0,0" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
<Label Name="Contacts" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
<Label Name="Groups" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
</StackPanel>
</StackPanel>
</Border>
Scenario: There is a border, It's default width is zero, It will raise to 165 in another trigger which works fine, I want to animate it to zero again when labels are clicked, but I can not access the width of that border in lables style
You claim that you use triggers in multiple labels so you can define trigger one time like this
<Storyboard x:Key="animation">
<DoubleAnimation Storyboard.TargetName="ListsMenu" Storyboard.TargetProperty="Height" To="86" Duration="0:0:.6"/>
</Storyboard>
and later when you need you can call it
<EventTrigger RoutedEvent="MouseEnter" >
<BeginStoryboard Storyboard="{StaticResource animation}"/>
</EventTrigger>
But I'm a bit confused what you meant to acquire.
You cannot get access in style since style does not have a namescope, as it was said. If it changes label's width you could do this in style like this
<Style x:Key="style_button" TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="300" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
but it invokes by StoryBoard.TargetName to another object.
try :
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.Target="{Binding ElementName=Menu}"
Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
TargetName is only valid inside Templates.

WPF XAML Grid Visibility Trigger

I have a status message located on the first row of my grid and I want it to slide in and out when the visibility changes.
The first visibility trigger works great and slides the first grid row open quickly. As soon as I add the 'Collapsed' trigger, nothing works at all. How do I reverse the animation to slide closed when the visibility is set to collapsed?
<Grid Grid.Row="0" Height="55" Visibility="{Binding StatusMessageVisibility, Mode=TwoWay}">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="55" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Collapsed">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="55" To="0" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="Hi There" />
</Grid>
You should remove the Visibility binding in your grid and use a DataTrigger that binds to the StatusMessageVisibility property. If you bind the grid's visibility then once it's collapsed it's collapsed and you won't be able to see the animation.
Also, instead of having two data triggers with EnterActions, use a single data trigger that also has an ExitAction for the collapsed state:
<Grid Grid.Row="0" Height="55">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusMessageVisibility}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="55" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="55" To="0" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="Hi There" />
</Grid>

WPF animation issue on button click

I would like to show some animation of button, before click event of button is fired. I am using following XAML to achieve that but it seems like Button.IsPressed trigger is getting fired after click event.
How to set trigger so it will show animation first before click event?
<Button x:Name="button"
Width="131"
Height="37"
Content="Button"
Margin="0,0,0,22"
Click="button_Click">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="Button.IsPressed" Value="True">
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
From="130"
To="0"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
EDIT: I tried this but same thing.
<EventTrigger RoutedEvent="PreviewMouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
From="130"
To="0"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
You didn't mention when exactly you want the animation to start but you could try and use PreviewMouseLeftButtonDown

WPF DoubleAnimation does not decrease the value after an animation

Consider the following simple WPF form, we will try to animate border1's Height:
This is the XAML for border1:
<Border Margin="3" BorderBrush="Black" BorderThickness="1" Name="border1">
<Border.Style>
<Style>
<Setter Property="Control.Height" Value="50" />
<Style.Triggers>
<DataTrigger Binding="{Binding X}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="100" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding X}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="200" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding X}" Value="3">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="300" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding X}" />
</Border>
X is a normal DependencyProperty and buttons do their work without problem and TextBlock's content inside border1 shows the changed value.
Now the problem is Height can be only Increased! For example by pressing 1, Height increases to 100, pressing 3 increases it to 300 but pressing 2 does not change the height.
If I set the initial border1's height to, for example, 400, all buttons can decrease it to 100, 200 or 300 but after this stage no animation can decrease border's height.
Am I missing some obvious point regarding WPF animation?
Update
Part of the issue is the storyboard in the DataTrigger is not being stopped, so (the last DataTrigger defined) is always holding the Height's 'animated' value.
Adding a StopStoryboard in the exit action fixes part of it. You can now switch between all three heights again... but the animation always starts from the Height's base value (which is clearly not what you want). You want it to start from where it was last set at.
The following MSDN article explains the second issue pretty well:
http://msdn.microsoft.com/en-us/library/aa970493.aspx
Good Solution- EventTriggers on your three buttons seem to work better and doesn't have the problems that the DataTrigger encounters (plus you don't even need the dependency property 'X' anymore!)
<Border Margin="3" BorderBrush="Black" BorderThickness="1" Name="border1">
<Border.Style>
<Style>
<Setter Property="Control.Height" Value="50" />
</Style>
</Border.Style>
<TextBlock Text="{Binding X}" />
</Border>
and
<StackPanel Orientation="Horizontal">
<Button Content="1" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="100"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="2" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="200"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="3" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="300"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
There must be a simpler way to do this, but I cannot recall if there is at the moment.
Otherwise this will work. You can set the ExitAction of your triggers to set back to the default value. Do this by using a DoubleAnimation with no To value.
i.e.,
<DataTrigger Binding="{Binding X}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.2" To="100" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>

Resources