How to Pause a Storyboard on MouseEnter and Resume on MouseExit - c#

I have a scrolling news ticker that is animated via a Storyboard. It currently works pretty well but I would like to pause it whenever the Mouse is over the TextBlock and then have it resume once the mouse is moved off of the TextBlock
<WrapPanel x:Name="Ticker" >
<TextBlock Text="{Binding NewsTicker, IsAsync=True}" FontSize="18" Foreground="White" >
<TextBlock.RenderTransform>
<TranslateTransform x:Name="translate" />
</TextBlock.RenderTransform>
<TextBlock.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="NewsTicker">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation From="{Binding ElementName=Ticker, Path=ActualWidth}" To="{Binding ElementName=Ticker, Path=ActualWidth, Converter={StaticResource NegConverter}}" Storyboard.TargetName="translate" Storyboard.TargetProperty="X" Duration="0:0:35" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.EnterActions>
<PauseStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger.EnterActions>
<EventTrigger.ExitActions>
<ResumeStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger.ExitActions>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</WrapPanel>
I've tried several combinations of Pause and Resume Storyboard as well as different RoutedEvents. I even removed the EventTrigger.ExitAction just to see if the pause would work but it made no difference.

Instead of using EnterActions/ExitActions use MouseEnter and MouseLeave events
<TextBlock.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="NewsTicker">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation ... />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<PauseStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<ResumeStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger>
</TextBlock.Triggers>
since events don't have state there is no enter/exit action there is just action when event occurs
EventTrigger Class
Trigger objects have the Setters, EnterActions, and ExitActions properties that apply changes or actions based on the state of certain properties, while EventTrigger objects start a set of Actions when a specified routed event occurs

Related

WPF element height binding lost after animation

In my example app, I have a inner grid's height binded to the main grid's height. When I maximise and minimise the window, their heights are the same. after I execute an animation that changes the inner grid's height from 100 to binding the main grid's height again, the binding is lost. This is evident because when I maximise the window, the inner grids height remains the same while the main grid's height changes to fit the fill height of the window
Why is this and how can I fix it so the inner grid retains the main grid's height after I've set it back to that after an animation.
Example app:
<Window.Resources>
<Storyboard x:Key="ShrinkSlider" x:Name="ShrinkSlider"
Completed="ShrinkSlider_Completed">
<DoubleAnimation
Storyboard.TargetProperty="Height"
Storyboard.TargetName="gridSlider"
DecelerationRatio="0.9"
From="100"
To="{Binding ActualHeight, ElementName=gridMain}"
Duration="00:00:00.5" />
</Storyboard>
<Storyboard x:Key="ExpandSlider" x:Name="ExpandSlider"
Completed="ExpandSlider_Completed">
<DoubleAnimation
Storyboard.TargetProperty="Height"
Storyboard.TargetName="gridSlider"
DecelerationRatio="0.9"
From="{Binding ActualHeight, ElementName=gridMain}"
To="100"
Duration="00:00:00.5" />
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click"
SourceName="btnShrink">
<BeginStoryboard x:Name="bsbShrinkSlider"
Storyboard="{StaticResource ShrinkSlider}" />
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click"
SourceName="btnExpand">
<BeginStoryboard x:Name="bsbExpandSlider"
Storyboard="{StaticResource ExpandSlider}" />
</EventTrigger>
</Window.Triggers>
<Grid x:Name="gridMain">
<Grid x:Name="gridSlider"
Background="#1f1f1f"
VerticalAlignment="Top"
Height="{Binding ActualHeight, ElementName=gridMain}">
</Grid>
<StackPanel VerticalAlignment="Bottom">
<Button Content="Shrink"
x:Name="btnShrink"
Height="20"
Click="BtnShrink_Click" />
<Button Content="Expand"
x:Name="btnExpand"
Height="20"
Click="BtnExpand_Click" />
</StackPanel>
</Grid>
Okay so I figured it out. I needed to set Storyboard FillBehaviour = "Stop"
Then I needed to recreate the binding on the Storyboard Completed event:
Binding binding = new Binding();
binding.Source = gridMain;
binding.Path = new PropertyPath(Grid.ActualHeightProperty);
gridSlider.SetBinding(Grid.HeightProperty, binding);
Here is the full code amended:
xaml:
<Window.Resources>
<Storyboard x:Key="ShrinkSlider" x:Name="ShrinkSlider"
Completed="ShrinkSlider_Completed"
FillBehavior="Stop">
<DoubleAnimation
Storyboard.TargetProperty="Height"
Storyboard.TargetName="gridSlider"
DecelerationRatio="0.9"
From="{Binding ActualHeight, ElementName=gridMain}"
To="100"
Duration="00:00:00.5" />
</Storyboard>
<Storyboard x:Key="ExpandSlider" x:Name="ExpandSlider"
Completed="ExpandSlider_Completed"
FillBehavior="Stop">
<DoubleAnimation
Storyboard.TargetProperty="Height"
Storyboard.TargetName="gridSlider"
DecelerationRatio="0.9"
From="100"
To="{Binding ActualHeight, ElementName=gridMain}"
Duration="00:00:00.5" />
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click"
SourceName="btnShrink">
<BeginStoryboard x:Name="bsbShrinkSlider"
Storyboard="{StaticResource ShrinkSlider}" />
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click"
SourceName="btnExpand">
<BeginStoryboard x:Name="bsbExpandSlider"
Storyboard="{StaticResource ExpandSlider}" />
</EventTrigger>
</Window.Triggers>
<Grid x:Name="gridMain">
<Grid x:Name="gridSlider"
Background="#1f1f1f"
VerticalAlignment="Top"
Height="{Binding ActualHeight, ElementName=gridMain}">
</Grid>
<StackPanel VerticalAlignment="Bottom">
<Button Content="Shrink"
x:Name="btnShrink"
Height="20" />
<Button Content="Expand"
x:Name="btnExpand"
Height="20" />
</StackPanel>
</Grid>
cs:
private void ShrinkSlider_Completed(object sender, EventArgs e)
{
gridSlider.Height = 100;
}
private void ExpandSlider_Completed(object sender, EventArgs e)
{
Binding binding = new Binding();
binding.Source = gridMain;
binding.Path = new PropertyPath(Grid.ActualHeightProperty);
gridSlider.SetBinding(Grid.HeightProperty, binding);
}

Handle Custom RoutedEvent in Event Trigger

I have the following tree
MainWindow -> Custom UserControl -> GroupBox -> ViewBox
I've added a Custom Routed Event to the UserControl, as :
public static readonly RoutedEvent ConnectedEvent =
EventManager.RegisterRoutedEvent("Connected", RoutingStrategy.Tunnel,
typeof(RoutedEventHandler), typeof(IQMasterControl));
public event RoutedEventHandler Connected
{
add { AddHandler(IQMasterControl.ConnectedEvent, value); }
remove { RemoveHandler(IQMasterControl.ConnectedEvent, value); }
}
The invocation of the event is following:
_masterViewModel.Connected += delegate ()
{
RoutedEventArgs ea = new RoutedEventArgs(IQMasterControl.ConnectedEvent, this);
this.RaiseEvent(ea);
};
I want to run some animation on ViewBox on "Connected" event using Event Trigger:
<Viewbox VerticalAlignment="Center"
Child="{StaticResource PDataIco}"
RenderTransformOrigin="0.5, 0.5" Stretch="Uniform">
<Viewbox.RenderTransform>
<!-- the transform a name tells the framework not to freeze it -->
<RotateTransform x:Name="noFreeze" />
</Viewbox.RenderTransform>
<Viewbox.Triggers>
<EventTrigger RoutedEvent="local:IQMasterControl.Connected">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)" To="360" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Viewbox.Triggers>
</Viewbox>
I have checked that the event is raised, but ViewBox does not seem to be receiving the event. So, no animation is started.
If I change the RoutedEvent property of the EventTrigger to another (f.e. Loaded) the animation works fine.
Can anyone help me on this?
You are raising the event for the UserControl.
It is not raised for the ViewBox.
There are two ways to go:
Both require adding name for the ViewBox:
<Viewbox VerticalAlignment="Center" x:Name="ViewBox"
Child="{StaticResource PDataIco}"
RenderTransformOrigin="0.5, 0.5" Stretch="Uniform">
<Viewbox.RenderTransform>
<!-- the transform a name tells the framework not to freeze it -->
<RotateTransform x:Name="noFreeze" />
</Viewbox.RenderTransform>
<Viewbox.Triggers>
<EventTrigger RoutedEvent="local:IQMasterControl.Connected">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)" To="360" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Viewbox.Triggers>
</Viewbox>
1.Raise it for the ViewBox:
_masterViewModel.Connected += delegate ()
{
RoutedEventArgs ea = new RoutedEventArgs(IQMasterControl.ConnectedEvent, this);
ViewBox.RaiseEvent(ea);
};
OR
2.Handle it in UserControl:
<UserControl.Triggers>
<EventTrigger RoutedEvent="local:IQMasterControl.Connected">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.Target="{Binding ElementName=Viewbox}" Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)" To="360" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</UserControl.Triggers>

Invoking animation in a different xaml control on button click WITHOUT resorting to ViewModels

I have the following xaml:
<Button Content="{Binding Header}" Background="Blue" Name="PanelHeader">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Stackpanel"
Storyboard.TargetProperty="Height"
From="0" To="100" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
I want to move the storyboard animation to the stack panel declaration and simply invoke that animation from the button click.
Most examples I've found resort to using the viewmodel or other codebehind interjections.
this example doesn't use code behind nor ViewModel
http://www.java2s.com/Tutorial/VB/0290__Windows-Presentation-Foundation/StartandstopanAnimationwithButtonactions.htm

How to assign the height property of the parent container to the to parameter of DoubleAnimation in xaml?

As the title says, I want to know how to assign the height(or ActualHeight) of the container to the To parameter/attribute of DoubleAnimation in xaml?
Here is my code:
<Canvas Name="animCanvas">
<Rectangle
Name="animSquare"
Canvas.Left="0"
Fill="AliceBlue"
Stroke="Bisque"
StrokeThickness="5"
Width="100"
Height="100"
>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="animSquare"
Storyboard.TargetProperty="(Canvas.Top)"
From="0"
To="500" <!-- I want to set it to the containers height -->
Duration="0:0:1"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
You can apply Binding to the To property of double animation like this :
To="{Binding ElementName=animSquare, Path=Height}"
I think this would solve your issue.

How to start a storyboard which is attached to a control inside a data template?

I have a storyboard inside a data template. I want to start this only for a certain condition. In my case whenever the no. of seconds in the clock is 59 the storyboard should start.
Below you can see the storyboard as well as the control to which the animation is applied :
<!-- Minute Hand -->
<Image
Source="{Binding Time, Converter={StaticResource MinHandBackground}}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Canvas.Left="118"
Canvas.Top="118">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform
X="-11"
Y="-90" />
<RotateTransform
x:Name="minHandTransform" />
</TransformGroup>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger
RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard
x:Name="myStoryboard2">
<DoubleAnimation
x:Name="minuteAnimation"
Storyboard.TargetName="minHandTransform"
Storyboard.TargetProperty="Angle"
Duration="0:0:1"
From="{Binding Time, Converter={StaticResource minuteHandTransform}}"
To="{Binding Time, Converter={StaticResource minuteHandToTransform}}"
RepeatBehavior="1x">
<DoubleAnimation.EasingFunction>
<SineEase
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Currently the BeginStoryboard starts it immediately on load. I want to start it when the number of seconds is 59 but i cant access the storyboard in the back end as the control is within a data template.
Can anyone please help me out here.....i have been banging my head about this problem for a lot of days now!!
The easiest way would be to move the entire contents of the DataTemplate to a separate UserControl so you would get easier access to all named elements.

Categories

Resources