It seems like it should be so simple. I've read dozens of links and I can't get anything to animate the position. I believe the closest code I can write so far is this:
Storyboard storyboard = new Storyboard();
TranslateTransform trans = new TranslateTransform() { X = 1.0, Y = 1.0 };
myCheckbox.RenderTransformOrigin = new Point(0.5, 0.5);
myCheckbox.RenderTransform = trans;
DoubleAnimation moveAnim = new DoubleAnimation();
moveAnim.Duration = TimeSpan.FromMilliseconds(1200);
moveAnim.From = -1;
moveAnim.To = 1;
Storyboard.SetTarget(moveAnim, myCheckbox);
Storyboard.SetTargetProperty(moveAnim, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
storyboard.Completed += new System.EventHandler(storyboard_Completed);
storyboard.Children.Add(moveAnim);
storyboard.Begin();
No errors are thrown.
The completion callback does get called.
If I animate opacity in a similar fashion it works fine.
How can I simply animate a UIElement's position with code??
The comment from xyzzer was correct. The cause of the confusion was because the coordinates for RenderTransformOrigin use (0,1) relative to the element. The actual transforms (e.g. TranslateTransform) use pixels as units.
Related
I have several boxes from type Windows.UI.Xaml.Controls.Control with different sizes. I wanna transform a few of them vertically. Like shown in the picture.
I'm struggling doing this.I'm sure that should not be very difficult but I don't get it...
Btw. I wanna do that in code behind not in XAML.
Many thanks for your help.
Cheers
Daniel
edit:
DoubleAnimation scaleAnimation = new DoubleAnimation();
scaleAnimation.From = startHeight;
scaleAnimation.To = this.ClientHeight * Percentage;
scaleAnimation.Duration = TimeSpan.FromMilliseconds(500);
scaleAnimation.EasingFunction = new QuarticEase() { EasingMode = EasingMode.EaseOut };
Storyboard storyScaleX = new Storyboard();
storyScaleX.Children.Add(scaleAnimation);
Storyboard.SetTarget(storyScaleX, slice);
scaleAnimation.EnableDependentAnimation = true;
Storyboard.SetTargetProperty(storyScaleX, "Height");
You can apply a TranslateTransform to the LayoutTransform or RenderTransform of the element (depending on what you need). e.g.
element.LayoutTransform = new TranslateTransform(0, 100)
If the effect you require depends on the height of the element, use the element's ActualHeight as the value to translate by.
I'm trying to animate some rotations in 3D with WPF and if I trigger them manually (on click) everything is fine, but if I compute the movements that should be made on the Viewport3D all animations seem to go off at the same time.
The code that computes the movements is as follows:
for(int i=0; i<40; i++){
foo(i);
}
Where the foo(int i) looks like:
//compute axis, angle
AxisAngleRotation3D rotation = new AxisAngleRotation3D(axis, angle);
RotateTransform3D transform = new RotateTransform3D(rotation, new Point3D(0, 0, 0));
DoubleAnimation animation = new DoubleAnimation(0, angle, TimeSpan.FromMilliseconds(370));
rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, animation);
The computation of axis and angle is not something time consuming, simple attributions, so I guess the problem is that all animations trigger the next frame since the computations are already done when the current frame is "over".
How can I display those animations sequentially, rather than all at once, in code (not XAML)?
PS: everything is in C#, no XAML.
You may add multiple animations to a Storyboard and set each animation's BeginTime to the sum of the durations of the previous animations:
var storyboard = new Storyboard();
var totalDuration = TimeSpan.Zero;
for (...)
{
var rotation = new AxisAngleRotation3D(axis, angle);
var transform = new RotateTransform3D(rotation, new Point3D(0, 0, 0));
var duration = TimeSpan.FromMilliseconds(370);
var animation = new DoubleAnimation(0, angle, duration);
animation.BeginTime = totalDuration;
totalDuration += duration;
Storyboard.SetTarget(animation, rotation);
Storyboard.SetTargetProperty(animation, new PropertyPath(AxisAngleRotation3D.AngleProperty));
storyboard.Children.Add(animation);
}
storyboard.Begin();
Note that i haven't tested the code above, so sorry for any faults.
Or you create your animations in a way that each animation (starting from the second one) is started in a Completed handler of the previous one.
The purpose of the code below is that a thumb follows a horizontal mouse movement. The code is called upon a mouse event, so the target value of the animation gets updated continuously.
In the code, offset is the current mouse horizontal position. The problem is, that the animation of the thumb doesn't fully animate to the specified offset, but always seems to be stopping at a value smaller or higher (depending if the mouse is dragged left or right).
The SeekAlignedToLastTick() influences the behavior of the animation, although I couldn't figure out what this function does by reading the documentation.
How can I animate the thumb, so that it follows smoothly the drag event?
private Storyboard _thumbStoryboard;
private DoubleAnimation _thumbAnimation = new DoubleAnimation();;
private CompositeTransform _thumbTransform = new CompositeTransform();
private void UpdateUserInterface(double offset)
{
var thumbItem = Thumb as FrameworkElement;
if (_thumbStoryboard == null)
{
Storyboard.SetTarget(_thumbAnimation, _thumbTransform);
_thumbStoryboard = new Storyboard();
_thumbStoryboard.Children.Add(_thumbAnimation);
thumbItem.RenderTransform = _thumbTransform;
_thumbStoryboard.Duration = new Duration(TimeSpan.FromMilliseconds(100));
_thumbAnimation.EasingFunction = new ExponentialEase();
}
double from = _thumbTransform.TranslateX;
_thumbStoryboard.Stop();
Storyboard.SetTargetProperty(_thumbAnimation, new PropertyPath("TranslateX"));
_thumbAnimation.From = from;
_thumbAnimation.To = offset;
_thumbStoryboard.Begin();
_thumbStoryboard.SeekAlignedToLastTick(TimeSpan.Zero);
}
I've tried to solve your issue, So I've created a Silverlight application and added a Border element for testing.
<Border x:Name="Thumb" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50" height="25" Background="#ff0000" />
There was no need to set the "From" Property, since the DoubleAnimation object could automatically continue from the current Value to the "To" Property.
And you were setting the Duration to the Storyboard, which causes the DoubleAnimation to Cutoff its animation without reaching the "To" Value, You need to set the Duration Property to the DoubleAnimation itself instead.
Also there was no need to call _thumbStoryboard.Stop(), because it will reset the current animation to the first TranslateX Value.
Here is the updated "UpdateUserInterface" function code with comments:
private void UpdateUserInterface(double offset) {
var thumbItem = Thumb as FrameworkElement;
if ( _thumbStoryboard == null ) {
// UpdateLayout Method is update the ActualWidth Properity of the UI Elements
this.UpdateLayout();
// Applying the CompositeTransform on "thumbItem" UI Element
thumbItem.RenderTransform = _thumbTransform;
// Setting the Render Transform Origin to be the Center of X and Y
thumbItem.RenderTransformOrigin = new Point(0.5d, 0.5d);
// Setting the target of the DoubleAnimation to be the Thumb CompositeTransform
Storyboard.SetTarget(_thumbAnimation, _thumbTransform);
// Setting the Targeted Properity of the DoubleAnimation to be The "TranslateX" Properity
Storyboard.SetTargetProperty(_thumbAnimation, new PropertyPath("TranslateX"));
// Used QuinticEase instead of ExponentialEase
// and Added EaseOut to make the animation be more smoother.
_thumbAnimation.EasingFunction = new QuinticEase(){ EasingMode = EasingMode.EaseOut };
// Initializing the Storyboard
_thumbStoryboard = new Storyboard();
// Specifing the Duration of the DoubleAnimation not the StoryBoard
_thumbAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
// Adding the DoubleAnimation to the Children of the Storyboard
_thumbStoryboard.Children.Add(_thumbAnimation);
}
// Calculate the New Centered Position
double newPos = offset - (thumbItem.ActualWidth / 2);
// Set the New DoubleAnimation "To" Value,
// There is no need to set the "From" Value since it'll automatically continue from the current TranslateX Value
_thumbAnimation.To = newPos;
// Begin the animation.
_thumbStoryboard.Begin();
}
Hope that helps you :)
Regards,
Monir Abu Hilal
I have an button which I move, using a storyboard and a translateTransform :
_animatedTranslateTransform = new TranslateTransform();
this.RegisterName("slide", _animatedTranslateTransform);
DoubleAnimation slideDown = new DoubleAnimation();
slideDown.By = 20;
slideDown.Duration = TimeSpan.FromSeconds(0.5);
Storyboard.SetTargetName(slideDown, "slide");
Storyboard.SetTargetProperty(slideDown, new PropertyPath(TranslateTransform.YProperty));
After some iterations, my button have moved from 60 px, for example. How can I replace it to its initial position ?
I tried this (I set -60 to simplify the explanation) :
TranslateTransform t = (TranslateTransform) _button.RenderTransform;
t.Y = -60;
But it doesn't work if the animation already occurred once.
UPDATE:
To keep future readers from being confused by the back-and-forth below, the correct answer is to do:
_button.RenderTransform = new TranslateTransform();
Original answer:
I would store the original position to be used whenever you want to reset. I don't think there's a concept of the "original state" for a control.
I have a Canvas which I would need to animate the RenderTransform property of. The start and end matrices will be abitrary, so I can't pre write the storyboard in XAML, so I'm trying to do it in code, I can't find any example of how to do this, below is my best try which does not work (it compiles and runs, but the rendertransform does not change).
Any suggestions on how this should be done?
MatrixAnimationUsingKeyFrames anim = new MatrixAnimationUsingKeyFrames();
MatrixKeyFrameCollection keyframes = new MatrixKeyFrameCollection();
DiscreteMatrixKeyFrame start = new DiscreteMatrixKeyFrame(fromMatrix, KeyTime.FromPercent(0));
DiscreteMatrixKeyFrame end = new DiscreteMatrixKeyFrame(toMatrix, KeyTime.FromPercent(1));
keyframes.Add(start);
keyframes.Add(end);
anim.KeyFrames = keyframes;
Storyboard.SetTarget(anim, World.RenderTransform);
Storyboard.SetTargetProperty(anim, new PropertyPath("Matrix"));
Storyboard sb = new Storyboard();
sb.Children.Add(anim);
sb.Duration = TimeSpan.FromSeconds(4);
sb.Begin();
I have implemented MatrixAnimation class which supports smooth translation, scaling and rotation animations. It also supports easing functions! Find at http://pwlodek.blogspot.com/2010/12/matrixanimation-for-wpf.html
I bumped into this problem this morning, although the solution I used won't cope with rotations or shearing. link
I managed to get matrixtransform working by setting rendersource and using beginanimation
something like this:
this.matrixTransform = new MatrixTransform();
this.canvas.RenderTransform = this.matrixTransform;
MatrixAnimationUsingKeyFrames anim = new MatrixAnimationUsingKeyFrames();
anim.KeyFrames = new MatrixKeyFrameCollection();
anim.Duration = TimeSpan.FromSeconds(4);
Matrix fromMatrix = new Matrix(2, 0, 0, 2, 0, 0);
Matrix toMatrix = new Matrix(3, 0, 0, 3, 0, 0);
anim.FillBehavior = FillBehavior.HoldEnd;
DiscreteMatrixKeyFrame start = new DiscreteMatrixKeyFrame(fromMatrix, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)));
DiscreteMatrixKeyFrame end = new DiscreteMatrixKeyFrame(toMatrix, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(4)));
anim.KeyFrames.Add(start);
anim.KeyFrames.Add(end);
this.matrixTransform.BeginAnimation(MatrixTransform.MatrixProperty, anim);
Not sure exactly how I'm going to do the interpolation for all the keyframes myself though :)