WPF - Stop Animation based on duration - c#

I have a storyboard declared in XAML just like this.
<Window.Resources>
<Storyboard x:Key="Storyboard1">
<DoubleAnimationUsingPath Duration="0:0:60" BeginTime="0:0:0 " Source="X" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="object_to_move">
<DoubleAnimationUsingPath.PathGeometry >
<PathGeometry Figures="M-1.75,-21 L6.75,-53.25 L29.75,-91.25 L49.75,-114.75 L56.75,-119.75"/>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
<DoubleAnimationUsingPath Duration="0:0:60" BeginTime="0:0:0" Source="Y" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="object_to_move">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M-1.75,-21 L6.75,-53.25 L29.75,-91.25 L49.75,-114.75 L56.75,-119.75"/>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
</Storyboard>
</Window.Resources>
What I am tryign to achieve is more control over the animation. So, first I call a reference of the storyboard in the back-end just like this.
// This works just fine.
var sub = FindResource("Storyboard1") as Storyboard;
sub.begin() // or Do something else
But I want also be able to stop the animation using the duration and specifically using milliseconds. For Instance, when object_to_move is moving for 0:0:35 milliseconds, I want it to stop when it hits the 0:0:50 mark.
What is the best way to achieve this?

Related

Button begins storyboard animation

** Edit Question * *
Storyboard s = (Storyboard)TryFindResource("kupmove");
s.Stop();
This doesnt stop the ongoing animation, how can i stop it ?
How can i begin the storyboard with a button click event in visual c# ?
Here is the code i am using to begin the storyboard :
Storyboard s = (Storyboard)TryFindResource("kupmove");
Storyboard.SetTargetName(s, "geometryModel3D");
s.Begin();
The animation does not begin and i get the following error :
'Children' property value in the path '(0).(1)[2].(2)' points to immutable instance of 'System.Windows.Media.Media3D.Transform3DCollection'
Here is my xaml storyboard :
<Storyboard x:Key="kupmove"
RepeatBehavior="Forever"
AutoReverse="True">
<Rotation3DAnimationUsingKeyFrames Storyboard.TargetProperty="(Model3D.Transform).(Transform3DGroup.Children)[2].(RotateTransform3D.Rotation)"
Storyboard.TargetName="geometryModel3D">
<EasingRotation3DKeyFrame KeyTime="0">
<EasingRotation3DKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</EasingRotation3DKeyFrame.EasingFunction>
<EasingRotation3DKeyFrame.Value>
<AxisAngleRotation3D Axis="0,1,0"
Angle="0" />
</EasingRotation3DKeyFrame.Value>
</EasingRotation3DKeyFrame>
<EasingRotation3DKeyFrame KeyTime="0:0:1">
<EasingRotation3DKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</EasingRotation3DKeyFrame.EasingFunction>
<EasingRotation3DKeyFrame.Value>
<AxisAngleRotation3D Axis="0,1,0"
Angle="179" />
</EasingRotation3DKeyFrame.Value>
</EasingRotation3DKeyFrame>
<EasingRotation3DKeyFrame KeyTime="0:0:2">
<EasingRotation3DKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</EasingRotation3DKeyFrame.EasingFunction>
<EasingRotation3DKeyFrame.Value>
<AxisAngleRotation3D Axis="0,-1,0"
Angle="1" />
</EasingRotation3DKeyFrame.Value>
</EasingRotation3DKeyFrame>
<EasingRotation3DKeyFrame KeyTime="0:0:3">
<EasingRotation3DKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</EasingRotation3DKeyFrame.EasingFunction>
<EasingRotation3DKeyFrame.Value>
<AxisAngleRotation3D Axis="0.009,-0.014,1"
Angle="90.005" />
</EasingRotation3DKeyFrame.Value>
</EasingRotation3DKeyFrame>
</Rotation3DAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Model3D.Transform).(Transform3DGroup.Children)[4].(TranslateTransform3D.OffsetX)"
Storyboard.TargetName="geometryModel3D">
<EasingDoubleKeyFrame KeyTime="0:0:2"
Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:3"
Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
* EDIT *
Suggested solutions doesnt work, actually i dont have an error with the previous code which triggers the storyboard, it works but I think because there is already an animation which has been initiated with the following code, causes the exception. Tell me a way to stop it when it has already been running :
> DoubleAnimation myAnimation = new DoubleAnimation();
> myAnimation.From = 1;
> myAnimation.To = 361;
> myAnimation.Duration = new >Duration(TimeSpan.FromMilliseconds(1000));
> myAnimation.RepeatBehavior = RepeatBehavior.Forever;
> rotateTransform.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myAnimation);
> geometryModel3D.Transform = rotateTransform;
I think i must stop this first stop this --> geometryModel3D.Transform = rotateTransform;
Because the animation i want to trigger has already been running with this code. How can i stop this before re-starting my storyboard with its own parameters which are declared in xaml code.
Stopping the geometryModel3D.Transform with a second parameter null value with BeginAnimation command, didnt help. First exception still occurs.
I think the problem initiates here :
Storyboard s = (Storyboard)TryFindResource("kupmove");
s.Stop();
This command doesnt stop the ongoing animation. So there must be something wrong thats why s.Begin() doesnt already trigger.
use x:Name instead of x:Key and call the Begin() method on that storyboard instance.
A moment ago, a gentleman posted an answer that I read that displayed:
"Try calling the storyboard by it's key, i.e., not using TryFindResource()"
Maybe you could try calling it with it's instantiated name, kupmove
I merely read this, and the user deleted his answer. Sorry if it doesn't help.
Also, as another user posted, above, use x:Name rather than key, to create a name for the storyboard, then call it using that initialized name.

How to animate at periodic intervals?

I have a storyboard which animates a minute hand to slide 6 degrees. Now i want the minute hand to slide at every 59th second forever. Are there any properties of storyboard or any other way that i can do it?
My storyboard
<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 minuteHandTransform}}"
RepeatBehavior="1x">
<DoubleAnimation.EasingFunction>
<SineEase
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
It doesn't sound like something you would want to rely on an animation to manage. Simply manage starting the animation from code behind every minute and you are done. It would be a lot easier to do it that way than using cryptic converters to control the From/To values. A Timeline such as a DoubleAnimation has a BeginTime property, but I have seen and verified reports of long animation durations (like 1 minute or above) hitting bugs in WinRT.
EDIT* (code samples)
Two simple ways I commonly use to trigger events at an interval are to use a DispatcherTimer with callback events or an async loop.
1. DispatcherTimer
var timer = new DispatcherTimer { Interval = TimeSpane.FromSeconds(1) };
timer.Tick += (s, e) => { /* do your stuff */ };
timer.Start();
2. async loop
RunMyLoop();
private async void RunMyLoop()
{
while (true)
{
/* do your stuff */
await Task.Delay(1000);
}
}
Try the following:
<Storyboard
x:Name="myStoryboard2">
<DoubleAnimation
x:Name="minuteAnimation"
Storyboard.TargetName="minHandTransform"
Storyboard.TargetProperty="Angle"
Duration="0:0:59"
From="{Binding Time, Converter={StaticResource minuteHandTransform}}"
To="{Binding Time, Converter={StaticResource minuteHandTransform}}"
RepeatBehavior="Forever">
<DoubleAnimation.EasingFunction>
<SineEase
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>

Simulate a series of button clicking in code and wait for effects to finish

I have series of buttons with EFFECTS that I'd like simulate clicking in code. The problem is that when I run "someFunction" the effects all trigger at about the same time. I'd like a button to click, then wait for the effect to finish then proceed on to the other buttons. I've tried using Thread.sleep but that doesn't work. Can anyone give me some pointers on how this can be accomplished? Also, is it possible to simulate clicking on multiple buttons at the same time?
Thanks for any help!
someFunction() {
button1.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
//System.Threading.Thread.Sleep(100); doesn't work wait for effect to finish
button2.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
//System.Threading.Thread.Sleep(100); doesn't work
button3.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
//System.Threading.Thread.Sleep(100); doesn't work
button4.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
<Button Name="buttonRed" Background="Red" BorderBrush="Black" BorderThickness="1"
Grid.Row="1" Grid.Column="0" Click="buttonRedClick">
<Button.BitmapEffect>
<BlurBitmapEffect x:Name="redBlur" Radius="0" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="redBlur"
Storyboard.TargetProperty="Radius"
From="0" To="40" Duration="0:0:0.3"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
Try looking at this article by Laurent Bugnion.
From above article:
Note that it is also possible to use the BeginTime property of the
animation object to delay an animation, for example in order to
cascade animations. Another way is to use the Completed event, which
can be set in the XAML code, or in the code-behind. This event implies
that the corresponding method must be present in the code-behind, thus
it is not applicable for pure XAML applications.
Thread.sleep will not work as it defines the sys.val realization, so you can use the following code:
there's the error : <EventTrigger RoutedEvent="Button.Click">
instead it should be: <Event.Trigger.Sleep RoutedEvent="Button.Click">

How do I make a Block Arrow blink?

I have a block arrow and i want to make it blink by just filling it with green. I would like to be able to stop it also. I have a right click menu to start it and stop it.
This is what i have so far. But i cant figure out how to start it. I tried to access it but i got an error:
All objects added to an IDictionary must have a
Key attribute or some other type of key associated with them. Line 11 Position 10.
Here is my xaml code:
<ed:BlockArrow x:Name="ArrowLeft" Fill="Green" HorizontalAlignment="Left" Height="29" Margin="142,0,0,-3" Orientation="Left" Stroke="#FF13FF00" VerticalAlignment="Bottom" Width="39" />
<Window.Resources>
<Storyboard x:Name="Blink" AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ArrowLeft"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01" Value="Green"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
So, in the right click menu to start it i have:
private void MenuItemLeft_Click(object sender, RoutedEventArgs e)
{
Storyboard blinkAnimation = TryFindResource("Blink") as Storyboard;
if (blinkAnimation != null)
{
blinkAnimation.Begin();
}
Is there a better way to do this? or what am i doing wrong?
WPF Resources are dictrionaries, hence everything within a Resource must have a key. You can add a key by adding an x:Key attribute. You can then locate your item by indexing into the Resource dictionary directly, Resources["MyKeyName"]
Regarding your method of implementation, it looks fine to me.

Stopping a programmatically instantiated storyboard

I have an EventTrigger that will stop a few storyboards that are defined in my XAML ... but now i need to stop a storyboard that I'm starting programmatically.
<UserControl.Resources>
<Storyboard x:Key="FadeIn"> ... Fade In Definition </StoryBoard>
<Storyboard x:Key="FadeOut"> ... Fade In Definition </StoryBoard>
</UserControl>
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard="{StaticResource FadeIn}" x:Name="FadeIn_BeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<StopStoryboard BeginStoryboardName="FadeIn_BeginStoryboard"/>
<StopStoryboard BeginStoryboardName="FadeOut_BeginStoryboard"/>
</EventTrigger>
</UserControl.Triggers>
Storyboard FadeOutStoryboard;
public void StopFadeOut() {
FadeIn_BeginStoryboard.Storyboard.Stop();
FadeOut_BeginStoryboard.Storyboard.Stop();
FadeOutStoryboard = (Storyboard) FindResource("FadeOut");
FadeOutStoryboard.Name="FadeOutStoryboard";
FadeOutStoryboard.Begin();
}
When I put a < StopStoryboard BeginStoryboardName="FadeOutStoryboard" /> it tells me it can't find FadeOutStoryboard. I'm fairly new to WPF programming, so there might be a better way to do this and I'm open to that. The StopFadeOut() method is being invoked by a parent that creates an instance of my usercontrol.
Move these lines to the constructor. You only need to run them once.
FadeOutStoryboard = (Storyboard)FindResource("FadeOut");
FadeOutStoryboard.Name = "FadeOutStoryboard";
Then add this line right after them.
RegisterName("FadeOutStoryboard", FadeOutStoryboard);

Categories

Resources