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
Related
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
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 am completely new to this forum, and still a beginner on WPF.
I am working on a project that requires the strokes on an inkcanvas to be animated. One of the animations required is "disappearing". I want to make the selected strokes gradually disappear with the click of a button, but appear at the end of the animation.
Since there is no opacity property for stroke, I tried using the ColorAnimation class along with storyboards. I have failed to make this code work, as I cannot target the strokes either using themselves or using their names, since they don't have any.
Right now I am thinking of implementing this system by gradually changing the color of the strokes to the color of the background, and at the end, resetting it back to its initial value. This is a costly loop, but I have no other ideas.
I would appreciate it if there are any other solutions you might share with me.
Thanks in advance.
Edit: I have not answered the comments, as I was dealing with other parts of the same project.
I have tried using the Alpha values that are stored in the DrawingAttributes, but I cannot change the value as it is not a variable. The same goes with RGB values. I have no idea on how to make the strokes disappear in a loop. I have already implemented most of the project, so I just need something to slowly make them disappear. Below you can find an example where I change the stroke itself to animate it.
private int dropOffset = 1;
private void DropAnimation()
{
m = new Matrix();
m.Translate(0, dropOffset);
animStrokes.Transform(m, false);
YChange += dropOffset;
dropOffset += 2;
}
And in another class, I have
public void AnimateStrokes(Dispatcher canvasDispatch)
{
Stopwatch initial = Stopwatch.StartNew();
while (initial.ElapsedMilliseconds < 2000)
{
foreach (Animation ai in AnimationList)
{
ai.animateSelected();
}
canvasDispatch.Invoke(new Action(() => { }), DispatcherPriority.Render);
Thread.Sleep(50);
}
foreach (Animation a in AnimationList)
{
a.undoAnimation();
}
canvasDispatch.Invoke(new Action(() => { }), DispatcherPriority.Render);
}
I know that it's not healthy to pass dispatcher like this, but it suffices for now.
Thanks again in advance.
InkCanvas1.DefaultDrawingAttributes.Color = Color.FromArgb(100, 0, 255, 255);
Might be a bit late but help for others none the less! The 100 is the alpha value which basically acts like an opacity value!! Mess with that and you will be able to change how transparent your strokes are :)
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 have a panel, within that panel are several rectangular controls (the number of controls vaires) I want the user to be able to move the controls around within the panel so that they can arrange the controls in the way that suits them best. does anyone have any resources i could read or simple tips which would get me headed down the right road?
thanks
I figured out a possible, simple method of moving a control in a drag/move style... Here are the steps.
Select an element in your control which you wish to be the movement area. This is the area in which, if me user holds the mouse down, the control will move. In my case it was a rectangular border at the top of the control.
Use the OnMouseDown event to set a boolean (in my case IsMoving) to true and the MouseUp event to set it to false
On the first MouseDown event, set some Point property (InitialPosition) using the following code
if (FirstClick)
{
GeneralTransform transform = this.TransformToAncestor(this.Parent as Visual);
Point StartPoint = transform.Transform(new Point(0, 0));
StartX = StartPoint.X;
StartY = StartPoint.Y;
FirstClick = false;
}
Now that you have the starting position, you need to get the position of the mouse relative to your movement control. This is so you dont end up clicking the middle of your header to move it and it instantly moves the top left of the control to the mouse pointer location. To do this, place this code in the MouseDown event:
Point RelativeMousePoint = Mouse.GetPosition(Header);
RelativeX = RelativeMousePoint.X;
RelativeY = RelativeMousePoint.Y;
Now you have the point the control originated at (startX and STartY), the position of the mouse within your movement control (RelativeX, RelativeY), we just need to move the control to a new location! There are a few steps involved in doing this. Firstly your control needs to have a RenderTransform which is a TranslateTransform. If you dont want to set this in XAML, feel free to set it using this.RenderTransform = new TranslateTransform.
Now we need to set the X and Y coordinates on the RenderTransform so that the control will move to a new location. The following code accomplishes this
private void Header_MouseMove(object sender, MouseEventArgs e)
{
if (IsMoving)
{
//Get the position of the mouse relative to the controls parent
Point MousePoint = Mouse.GetPosition(this.Parent as IInputElement );
//set the distance from the original position
this.DistanceFromStartX= MousePoint.X - StartX - RelativeX ;
this.DistanceFromStartY= MousePoint.Y - StartY - RelativeY;
//Set the X and Y coordinates of the RenderTransform to be the Distance from original position. This will move the control
TranslateTransform MoveTransform = base.RenderTransform as TranslateTransform;
MoveTransform.X = this.DistanceFromStartX;
MoveTransform.Y = this.DistanceFromStartY;
}
}
As you can guess, there is a bit of code left off(variable declarations etc) but this should be all you need to get you started :) happy coding.
EDIT:
One problem you may encounter is that this allows you to move the control out of the area of its parent control. Here is some quick and dirty code to fix that issue...
if ((MousePoint.X + this.Width - RelativeX > Parent.ActualWidth) ||
MousePoint.Y + this.Height - RelativeY > Parent.ActualHeight ||
MousePoint.X - RelativeX < 0 ||
MousePoint.Y - RelativeY < 0)
{
IsMoving = false;
return;
}
Place this code in your MouseMove event before the actual movement takes place. This will check if the control is trying to move outside the bounds of the parent control. The IsMoving = false command will cause the control to exit movement mode. This means that the user will need to click the movement area again to try to move the control as it will have stopped at the boundary. If you want the control to automatically continue movement, just take that line out and the control will jump back onto the cursor as soon as it is back in a legal area.
You can find a lot of inspiration here:
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part1.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part2.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part3.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part4.aspx