I have a Storyboard in XAML with ReapeatBehaviour="Forever". I run the storyboard when uploading files, which can vary quite a bit in size. I can stop the storyboard no problem when the file upload is complete, but what I'd like to do is run the storyboard just one more time, rather than stopping it midflow by calling Storyboard.Stop().
How do I accomplish this?
Ah sussed it - instead of calling Storyboard.Stop(), change the RepeatBehaviour property instead:
Storyboard11.RepeatBehavior = new RepeatBehavior(1.0);
Related
I wanted to know if is there a way to do something (call function...) every frames in a WPF application, like "update()" in Unity, or like "Application.Idle += new EventHandler(Application_Idle)" in a Winform app ?
Thanks.
Is it related purely to UI rendering events? If so, try looking into CompositionTarget.Rendering event.
It sounds like you probably want to just use a Dispatcher timer. How do I create a timer in WPF?
The update time in unity is based on many factors but generally at 60 frames a second so the timer interval would be something like 17ms. However you should know wpf doesn't really have frames like unity so there is no update equivalent. It only updates the layout when something moves/added/changed or you call InvalidateLayout to force it to do so.
maybe you could structure your windows constructor like this as in pygame, though I'm not sure it will work
bool running = true;
public someWindow() {
// setup
running = true;
while (running) {
// do stuff
}
}
I have a storyboard that I would like to run multiple times. Unfortunately, after it runs the first time, even when this code is hit, it doesn't animate again. It only animates the first time. I'm setting an initial value....even when I set the value before I run it again.
DoubleAnimationUsingKeyFrames animateTransformX = new DoubleAnimationUsingKeyFrames();
EasingDoubleKeyFrame initialTransformXKeyframe = new EasingDoubleKeyFrame();
initialTransformXKeyframe.KeyTime = TimeSpan.FromSeconds(0);
//Resetting the value before running it again.
initialTransformXKeyframe.Value = 0;
animateTransformX.KeyFrames.Add(initialTransformXKeyframe);
EasingDoubleKeyFrame animateTransformXKeyframe = new EasingDoubleKeyFrame();
animateTransformXKeyframe.KeyTime = TimeSpan.FromSeconds(0.5);
animateTransformXKeyframe.Value = aDynamicValueGoesHere;
animateTransformX.KeyFrames.Add(animateTransformXKeyframe);
Storyboard.SetTarget(animateTransformX, image);
Storyboard.SetTargetProperty(animateTransformX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"));
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(animateTransformX);
myStoryboard.Begin(this);
I feel like it is very simple but for the life of me I can't understand why. Any help would be appreciated.
EDIT
The reason I was doing it from codebehind is because storyboards are freezable and I have had trouble in the past with dynamic values in them. For simplicity I didn't put that I was using a dynamic value in the code; I updated the code above to show I'm using a dynamic value.
I tried adding the storyboard as XAML inside a resource with the use of a dynamic value. It works fine (and replays) if a non-dynamic value is used but it only plays once (the first time) when a dynamic value is used.
So, if {Binding OffsetX} were replaced with 50, for example, this works fine, and repeatedly.OffsetX gets updated through a property that implements INotifyPropertyChanged. Even if it were the same, if it were never updated, I would think it should still animate, just to the same value it animated to before (like it does with 50). But that isn't the case; it just doesn't animate again. So now I'm really lost.
<Storyboard x:Key="InitialAnimate">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)" Storyboard.TargetName="StoryImage">
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="{Binding OffsetX}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
EDIT 2
I didn't fix the problem but I found a workaround that works for my particular situation. I was getting this error in my output window:
System.Windows.Media.Animation Warning: 6 : Unable to perform action because the specified Storyboard was never applied to this object for interactive control.; Action='Stop'; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='28715394'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='6255521'; TargetElement.Type='System.Windows.Controls.Grid'
I found a few StackOverflow answers regarding it, including how to find what Storyboard caused the issue and another unanswered question.
I am still not sure how to fix my original issue, but here's my workaround. Since I only needed the animation to run when you restarted (an event that happens in my viewmodel), I use an event raised in the viewmodel and consumed in the parent view to remove and then re-add the UserControl that contains the animation.
In that UserControl, I trigger the storyboard through a ControlStoryboardAction tied to a Loaded EventTrigger. This makes the animation run when it loads, and it only ever runs the animation once during the life of the UserControl. I am able to use the dynamic values in XAML. Here's how I'm removing and readding the view from it's parent:
Parent View:
<grid x:Name="imageHolder></grid>
Parent Codebehind:
private void CreateAnimations(object sender, EventArgs e)
{
imageHolder.Children.RemoveAt(0);
Views.Image image = new Image();
image.Name = "image";
imageHolder.Children.Add(image);
}
You can't change the Value property at runtime the way you're doing after the edit.
This is because Animations are freezable objects. There is more
information in the MSDN Documentation, but basically it means you
can't use binding because properties in the frozen object (i.e. the
animation) cannot change.
To get around this limitation, you will need to do some or all of the
work in code-behind.
See Why Storyboard value wouldn't get from a resource or binding?
So You probably want to go back to the codebehind method.
So as a final answer, what I would do is when I want to run it again:
Remove the previous storyboard
create a whole brand new Storyboard(or just the animation) and run that.
I have started an Animation in a method and later I will have to stop it and complete it immediately if it has not completed then.
Vector diff = ...
DoubleAnimation aniX = new DoubleAnimation(diff.X, TimeSpan.FromSeconds(0.4));
DoubleAnimation aniY = new DoubleAnimation(diff.Y, TimeSpan.FromSeconds(0.4));
aniX and aniY to be stopped, if they have not completed.
I think you should use XAML code with some attached Trigger to stop it. To stop the animation programmatically. You can try using Begin to begin the Storyboard and Stop to stop it (don't use BeginAnimation):
Storyboard st = new Storyboard();
Storyboard.SetTarget(aniX, yourObject);
Storyboard.SetTargetProperty(aniX, yourPropertyPath);
st.Children.Add(aniX);
Storyboard.SetTarget(aniY, yourObject);
Storyboard.SetTargetProperty(aniY, yourPropertyPath);
st.Children.Add(aniY);
//begin
st.Begin();
//stop
st.Stop();
NOTE: The code above supposes you declare all the DoubleAnimations and Storyboard in procedural code (not in XAML code). I think you should declare them in XAML code to make it more concise, many class and stuff in WPF are optimized for XAML, so sometimes you can feel it's a little weird to look them in procedural code.
I am trying to get the current state of a storybaord.
I wanted something like if the storyboard have stopped play it again.
How should i go about getting the current state??
Below is my code to play teh storyboard :
void loadtime()
{
ringingAlarm.Begin();
}
Storyboard does not have any properties or methods that indicate its current state. However it does fire a Completed event when it has finished. You could create a simple wrapper around your Storyboard to track state, see this forum post for an example.
You can use Storyboard.GetCurrentState to get the ClockState. This ClockState is an enumeration that has a Stopped property, which is returned if your animation is stopped.
Or, you can create a wrapper as ColinE suggested.
I want a XAML page to perform an action once every 1/2 second.How would I do this?I'm thinking that I could use a Timer that starts when the page is loaded and resets once it gets to 500 milliseconds, but I'm not entirely sure how to do this.It doesn't have to be extremely precise.
You can use DispatcherTimer for example. For sample code look at documentation.
Did You Know… That You Can Create A Timer Using XAML Animation?