How to animate at periodic intervals? - c#

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>

Related

WPF - Stop Animation based on duration

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?

FadeIn and FadeOut Theme Animation works only once and only one way,why?

Anyone can help me please?
What I'm trying to do is, make a random quote fading in and out on my textblock.
I'm trying to use something called "FadeInThemeAnimation".
The thing is that I don't know how to make it work endlessly ,so that my random quote appears and disappears after the time a set up. I've tested a dozen attributes in xaml to see what happens but it seems like I have no clue what to do. At the moment the quote appears in the textblock(without any fade in effect) and slowly fades out, that is it. Please , how do I make it work. I'm pasting in a chunk of xaml code I have got.
<TextBlock x:Name="FamousQuoteTextBlock" Grid.Row="4" HorizontalAlignment="Stretch" Margin="5,5,5,5"
FontSize="15" TextAlignment="Justify" TextWrapping="Wrap" VerticalAlignment="Stretch"
Loaded="FamousQuoteTextBlock_Loaded"
FontFamily="Edwardian Script ITC"
DataContext="{Binding quote}"
Text="{Binding RandomQuote,Mode=TwoWay}"
/>
<StackPanel>
<StackPanel.Resources>
<Storyboard x:Name="EnterStoryboard">
<FadeInThemeAnimation Storyboard.TargetName="FamousQuoteTextBlock"/>
</Storyboard>
<Storyboard x:Name="ExitStoryboard">
<FadeOutThemeAnimation Storyboard.TargetName="FamousQuoteTextBlock"/>
</Storyboard>
</StackPanel.Resources>
</StackPanel>
I have tried to use something like speedratio,duratio and so on to see what happens but it didn't make any difference:(
My C# code behind:
private void FamousQuoteTextBlock_Loaded(object sender, RoutedEventArgs e)
{
Delay(sender, e);
}
private async void Delay(object sender, RoutedEventArgs e)
{
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(3));
generator_Tick(this, e);
DispatcherTimer generator = new DispatcherTimer();
generator.Tick += generator_Tick;
generator.Interval = TimeSpan.FromSeconds(5);
generator.Start();
}
private void generator_Tick(object sender, object e)
{
Quote quote = new Quote();
FamousQuoteTextBlock.DataContext = quote;
quote.GenerateQuote();
}
You can create your own animation like this :
<TextBlock x:Name="QuoteTextBlock" Text="My Quote here">
<TextBlock.Resources>
<Storyboard x:Key="FadeStoryboard">
<DoubleAnimation From="1" To="0.1" Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="QuoteTextBlock"
RepeatBehavior="Forever" AutoReverse="True">
<DoubleAnimation.EasingFunction>
<CubicEase/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</TextBlock.Resources>
</TextBlock>
And start it like this :
var storyboard = QuoteTextBlock.Resources["FadeStoryboard"] as Storyboard;
if (storyboard != null)
{
storyboard.Begin();
}
The small tricks you were looking for were AutoReverse which means that after the animation is performed, the reverse animation will follow up. If combined with RepeatBehaviour="Forever", it might achieve what you were looking for.
I've added an Ease. You can customize the easing function a lot. It means that the animation step won't be performed in a linear manner, but in this case will be cubic.
Some links which can provide you with more details on this matter :
MSDN : Quickstart - Animating your UI using XAML
MSDN : Storyboarded Animations

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.

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">

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