I need to animate multiple properties of one ui element at the same time.
For example, decreasing width and height of windows synchronously.
Any idea?
DoubleAnimation widthAnimation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromSeconds(5)
};
DoubleAnimation heightAnimation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromSeconds(5)
};
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(Window.WidthProperty));
Storyboard.SetTarget(widthAnimation, this);
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(Window.HeightProperty));
Storyboard.SetTarget(heightAnimation, this);
Storyboard s = new Storyboard();
s.Completed += FadeOut_Completed;
s.Children.Add(widthAnimation);
s.Children.Add(heightAnimation);
this.BeginStoryboard(s, HandoffBehavior.SnapshotAndReplace, true);
It will do animations step by step; height will change after width changes are complete! :|
After looking at your code I understand that you are trying to animate the width and height of Window simultenousely
But I would regret to tell you that since window is not an actual wpf component but a platform component. however content of window is completely controllable as expected via your code, but window is not. any such changes are routed through Pinvoke. and the issue you are facing is a known issue and the work around are bit complex
one solution is here, this uses pinvoke to animate the window's height and width
Animating a WPF window width and height
here is a bug for similar issue created at microsoft, result is (Closed, as Won't Fix)
https://connect.microsoft.com/VisualStudio/feedback/details/715415/window-width-height-animation-in-wpf-got-broken-on-net-framework-4-0
Extra
below is a sample which is not actually solving your problem but will help you to reduce the number of lines you need to perform such animations for other elements. It is a rewrite of your code in less lines
DoubleAnimation anim = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromSeconds(5)
};
border.BeginAnimation(Border.HeightProperty, anim);
border.BeginAnimation(Border.WidthProperty, anim);
try this code with any element except window, I used a border with some color filled
apologies for overlooking the Window in your code at first sight
Related
I am struggling in smoothing WPF animation
Actually my animation code is as follows:
private void AnimateX ( FrameworkElement element, double XMoveStart, double XMoveEnd, int secondX)
{
SineEase eEase = new SineEase();
eEase.EasingMode = EasingMode.EaseInOut;
Storyboard sb = new Storyboard();
DoubleAnimation daX = new DoubleAnimation(XMoveStart, XMoveEnd, new Duration(new TimeSpan(0, 0, 0, secondX, 0)));
daX.EasingFunction = eEase;
Storyboard.SetTargetProperty(daX, new PropertyPath("(Canvas.Left)"));
sb.Children.Add(daX);
element.BeginStoryboard(sb);
}
The above code is a method to move an object horizontally with sine ease. When only one object is moving, it is OK. However, whenever two or more objects move together (call AnimateX method on another object when the previous animation has not yet completed), the animation starts to become jittery. By jittery I mean, the objects are kind of shaking during the course of animation.
I faced the same problem many times. I found out that depending on the objects you add to your canvas, WPF will often have to regenerate representations of these objects on every frame (which I believe might be your case, depending on the type of UI elements you are manipulating). You can solve the jitter issue by telling WPF to cache a representation of your canvas in a bitmap. This is done very simply as follows, in your Xaml definition of the canvas:
<Canvas ...Your canvas properties...>
<Canvas.CacheMode>
<BitmapCache />
</Canvas.CacheMode>
...Your objects...
</Canvas>`
This reduces the load on your WPF application, as it simply stores the representation of your objects as a bitmap image, and as a consequence your application does not have to redraw them on every frame. This solution only works if your animation is applied externally to the canvas, and that there is no on-going local animations applying to the individual objects drawn in your canvas. You'll want to create separates canvases with their own caching if other animations in your code move the two objects with respect to each other.
Note that some UI elements will not be eased by this strategy. However, I've seen this strategy work efficiently for many elements, including TextBoxes and the likes, as well as geometric shapes. In any case, it's always worth the try.
Secondly, if caching local representations does not suffice, then you might want to have a look at the performance of your code and see if any process could be responsible for blocking the UI momentarily. There is no uniform solution regarding this aspect and it depends on what else is putting strain on your application UI. Cleaning the code and using asynchronous processes where relevant could help.
Finally, if, after all these checks the overall demand on your application remains too high, you can somewhat remove some strain on the application by reducing its general frame rate, the default being 60. You can try 30 or 40 and see if this improves the jittering by including the following code in your initialization:
Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata { DefaultValue = 40 });
Just a guess, but what happens if you directly animate the property, withoud using a Storyboard?
private void AnimateX(FrameworkElement element, double xMoveStart, double xMoveEnd, double durationSeconds)
{
DoubleAnimation animation = new DoubleAnimation
{
From = xMoveStart,
To = xMoveEnd,
Duration = TimeSpan.FromSeconds(durationSeconds),
EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut }
};
element.BeginAnimation(Canvas.LeftProperty, animation);
}
I have a wpf application and image inside a canvas. The image is placed in 0,0.
I need to animate the image moving from 0,0 to 500,200 and in the same time growing (I like to make an effect like coming from far to near).
If I do this:
TranslateTransform ttx = new TranslateTransform();
TranslateTransform tty = new TranslateTransform();
DoubleAnimationUsingKeyFrames dax = new DoubleAnimationUsingKeyFrames();
dax.KeyFrames.Add(new LinearDoubleKeyFrame(500, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1))));
DoubleAnimationUsingKeyFrames day = new DoubleAnimationUsingKeyFrames();
day.KeyFrames.Add(new LinearDoubleKeyFrame(200, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1))));
TransformGroup tg = new TransformGroup();
tg.Children.Add(ttx);
tg.Children.Add(tty);
krug.RenderTransform = tg;
ttx.BeginAnimation(TranslateTransform.XProperty, dax);
tty.BeginAnimation(TranslateTransform.YProperty, day);
And this works fine. It animates the translation of the image "krug" from 0,0 to 500,200.
But when I add logic for zooming the image while translating like this:
ScaleTransform zoom = new ScaleTransform();
DoubleAnimationUsingKeyFrames zoomTimeline = new DoubleAnimationUsingKeyFrames();
zoomTimeline.KeyFrames.Add(new LinearDoubleKeyFrame(2, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1))));
tg.Children.Add(zoom);
zoom.BeginAnimation(ScaleTransform.ScaleXProperty, zoomTimeline);
zoom.BeginAnimation(ScaleTransform.ScaleYProperty, zoomTimeline);
Then the image does not stop to 500, 200 but goes more far. If the zoom factor is bigger, the translation goes more future. How can I control the animation to stop at 500,200 ?
The problem you run into when combining scale and translate transforms is that it will scale the translate transform from the point of origin(ScaleTransform.CenterX, ScaleTransform.CenterY)
For example, if you want to slide it to the right by 50, and double it's scale, it will actually move a net distance of 100.
Try animating the ScaleTransform.CenterX and ScaleTransform.CenterY to match your translate transform. I believe that would let you scale on the fly like you want.
Put the animations in a single storyboard and set the duration of the storyboard to control the length of the animations.
Then start the storyboard.
I found a solution. I created separate user control that only does ScaleTransform. Then I apply TranslateTransform on the user control.
I implemented lightbox effect with window's opacity change whilst fading in/out. When I have my window maximized this effect has big delay or when I use duration property then opacity change is not smooth.
I manage this eg. with like here:
DoubleAnimation animate = new DoubleAnimation();
animate.From = 1.0;
animate.To = 0.5;
animate.Duration = new Duration(TimeSpan.FromSeconds(0));
this.BeginAnimation(Window.OpacityProperty, animate); // main window
Window1 win = new Window1(); // new window to get focus
win.ShowDialog();
Tell me please, if you know, does this effect works on GPU by default? If not, can I manage this somehow?
The maximization issue sounds like the computer might have performance issues, and the Duration issue exists because you set it to 0, a zero second animation is instant, of course it is not smooth.
I want to show new window with animation that enlarge the size from 0 to 300. In the Loaded event, I put this code:
DoubleAnimation heightAnim = new DoubleAnimation(0,300,TimeSpan.FromSeconds(1));
BeginAnimation(HeightProperty,heightAnim);
DoubleAnimation WidthAnim = new DoubleAnimation(0,300,TimeSpan.FromSeconds(1));
BeginAnimation(WidthProperty,WidthAnim);
This code work fine. But the construction starts on top - left corner of window and it expanded to right and down until it got 300 * 300 size.
I want it to start from the center and expand from there to all 4 sides.
How can I do it?
p.s. after trying Jogy answer, I realized that maybe better approach to get this effect is to apply animation on ScaleX and ScaleY instead of applying animation on the window size. Because when animation on window size , user will see only part of the content until it got to full size.
What do you think of ? What's better approach?
Thanks in advance!
In order to get this effect, I realized It's better use ScaleTransform. On Loaded event the code should be like this:
ScaleTransform scaleTransform = new ScaleTransform();
the 'grid' is element under Window and it's HorizontalElignment and VerticalElignment set to 'Center'
grid.LayoutTransform = scaleTransform;
DoubleAnimation heightAnim = new DoubleAnimation(0,1,TimeSpan.FromSeconds(1));
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty,heightAnim);
DoubleAnimation WidthAnim = new DoubleAnimation(0,1,TimeSpan.FromSeconds(1));
scaleTransform.BeginAnimation((ScaleTransform.ScaleXProperty,WidthAnim);
Add two more animations, for the Top and Left properties, like this:
DoubleAnimation topAnim = new DoubleAnimation(this.Top, this.Top - 150, TimeSpan.FromSeconds(1));
BeginAnimation(TopProperty, topAnim);
DoubleAnimation leftAnim = new DoubleAnimation(this.Left, this.Left - 150, TimeSpan.FromSeconds(1));
BeginAnimation(LeftProperty, leftAnim);
You may also want to synchronize the animations, by putting them in a Storyboard.
OK, I'm making a GUI for my MP3 player using WPF and I have a border that enlarges its width property for every second of the played track, thus making a "Progress Bar" for the currently played song. I named the border ProgressBarBorder. After the whole playlist is complete, I wanted to use a DoubleAnimation to fade out the border. Now, if I start the player again, the border reacts as it's supposed to (meaning the width starts from 0 and progresses to the end of the song), but the opacity property for some strange reason stays 0.0 (that is the value that DoubleAnimation sets). I have explicitly coded
ProgressBarBorder.Opacity = 1.0;
in the method that starts the playback. Nevertheless, it stays invisible. Now, if I don't use DoubleAnimation and just write
ProgressBarBorder.Opacity = 0.0;
when the playlist is complete, it does go back to 1.0 when I start the player again. This is the reason why I am positive that the animation is the one causing the problem. Also, isn't the property supposed to go back to it's original state after the animation is finished? If yes, my border should become visible automatically after the animation is complete.
Here's my partially pseudo-code:
if (TrackIsComplete)
{
DoubleAnimation Fading = new DoubleAnimation();
Fading.From = 1.0;
Fading.To = 0.0;
Fading.Duration = TimeSpan.FromSeconds(3);
ProgressBarBorder.BeginAnimation(Border.OpacityProperty, Fading);
}
and
private void PlayTrack()
{
ProgressBarBorder.Opacity = 1.0;
Play();
....
}
Could anyone help please? Thanks.
The animation is holding on to its target value. To free the dependency property of any animations, do a BeginAnimation with a null value:
private void PlayTrack()
{
ProgressBarBorder.BeginAnimation(Border.OpacityProperty, null);
ProgressBarBorder.Opacity = 1.0;
....
}
When an Animation ends, it continues holding the value. This is what is causing the behavior you noticed, where setting the property does not appear to update it. Here's some info on how to set a property after an animation has been applied to it.
Play around with the FillBevior of your animation timeline. This might help:
http://msdn.microsoft.com/en-us/library/system.windows.media.animation.fillbehavior.aspx