I followed a tutorial on rotating a picture bound to a button and it should be pretty simple, yet I can't figure it out.
private void rotateMenu()
{
int rotateAngle;
if (menuState) rotateAngle = -90;
else rotateAngle = 90;
DoubleAnimation myanimation = new DoubleAnimation(0, rotateAngle, new Duration(TimeSpan.FromMilliseconds(222)));
var rotateTransform = new RotateTransform(rotateAngle, 24.5, 24.5);
menuButtonImage.RenderTransform = rotateTransform;
rotateTransform.BeginAnimation(RotateTransform.AngleProperty, myanimation);
}
First button press - rotate the picture by 90 degrees. Simple, works.
Now I want to either reverse the rotation or rotate another 90 degrees.
Animation works fine but the outcome always switches back to the picture being rotated 90 degrees, no matter what I set the second rotateAngle to.
Basically what I get is in the first part of the picture, want I need in the second part.
What am I doing wrong here? Why can't I rotate the picture again? I tried with -90, 90 and lots of other values, e.g. 45 degrees, yet no rotation happening?
That's because the picture isn't actually rotated, but it's only rendered rotated. Until the rendertransform gets a new transform, it will stay rotated.
This will rotate it back, So you could try:
DoubleAnimation myanimation2 =
new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(222)));
only use the toValue parameter overload to rotate it back.
I would make something like:
// initial angle is 0
RotateTransform _rotateTransform = new RotateTransform(0.0, 24.5, 24.5);
Duration _rotationSpeed;
private void CreateRotation()
{
_rotationSpeed = new Duration(TimeSpan.FromMilliseconds(222));
menuButtonImage.RenderTransform = _rotateTransform;
}
private void rotateToSide()
{
DoubleAnimation myanimation = new DoubleAnimation(90, _rotationSpeed);
_rotateTransform.BeginAnimation(RotateTransform.AngleProperty, myanimation);
}
private void rotateToDefault()
{
DoubleAnimation myanimation = new DoubleAnimation(0, _rotationSpeed);
_rotateTransform.BeginAnimation(RotateTransform.AngleProperty, myanimation);
}
For your information (as I can read from your code) You don't have to change the Angle property of the rotateTransform object. The rotateTransform.BeginAnimation will alter the property.
Related
So, I am trying to move my rectangular boxes around a grid like this -
For this, I am using a Story board.
I am using DoubleAnimation to move the boxes on X-Axis and Y-Axis in one of my Class. I am calling this class from MainWindow class. But, for every box, and for every turn, I have to create a new Double animation, assign the offset values, the start time, the duration etc. like this -
//Code to move Boxes 1-4 to first grid point in their path
TranslateTransform moveTransform = new TranslateTransform();
moveTransform.X = 0;
moveTransform.Y = 0;
x.RenderTransform = moveTransform;
Storyboard s = new Storyboard();
DoubleAnimation Move1= new DoubleAnimation();
Move1.From = 0;
Move1.To = xPosition; // calculate correct offset here
Move1.Duration = new Duration(TimeSpan.FromSeconds(hops));
if (x==Box2)
{
Move1.BeginTime = TimeSpan.FromSeconds(5);//For Box 2, the first move will be across Y-Axis and hence X-Axis move will be delayed by 5 seconds.
}
else
{
Move1.BeginTime = TimeSpan.Zero;
}
Storyboard.SetTarget(Move1, x);
Storyboard.SetTargetProperty(Move1, new PropertyPath("(UIElement.RenderTransform).(X)"));
s.Children.Add(Move1);
I know there is a way to define the path to reach the destination from source, but I am not sure how to do it? Also, I am not sure if what I am doing here is the optimal way.
So, my question is -
What is the better way to do this? How can we define paths for animation?
I am a newbie to C# so please do not mind if this sounds silly.
Thank you!
I was able to do it without using path.
For every move, I created a new double animation.
So for Left - Down - Right movement, I created 3 double animations with
X-Axis, Y-Axis and X-Axis again.
I want to move a shape using an animation and am currently using the following code. However this does not result in the object actually moving, it just seems to change the location the object is rendered in, which is expected given we are setting the crossHair.RenterTransform.
EDIT:
To clarify - I am using the animation to simulate what an input file instructions contain and as a result think I can only build the animations in code when parsing the input files. I could be wrong about this but can't see how one could do this in XAML. The input file format is not in XAML.
Because there are a number of sequential moves contained in the input file I an currently using the shapes current position as the start point for the next animation, however this does not work because it seems the animation is not actually moving the shape.
As a work around I am now changing the shapes actual position in the animations completion handler. This seems to be working.
So the question remains as to how can I use the same transform to actually move the shape rather than simply rendering it in a different position ?
private Storyboard MoveCrossHairToPoint(double x, double y)
{
// Adjust for crosshair size so its centered on the x
double xPos = x;
double yPos = y;
double xStart = Canvas.GetLeft(crossHair)+crossHair.Width/2;
double yStart = Canvas.GetTop(crossHair)+crossHair.Height/2;
// Create a NameScope for the page so that
// we can use Storyboards.
NameScope.SetNameScope(this, new NameScope());
// Create a MatrixTransform. This transform
// will be used to move the crossHair.
MatrixTransform crossHairMatrixTransform = new MatrixTransform();
crossHair.RenderTransform = crossHairMatrixTransform;
// Register the transform's name with the page
// so that it can be targeted by a Storyboard.
this.RegisterName("MoveCrossHairMatrixTransform", crossHairMatrixTransform);
// Create the animation path.
PathGeometry animationPath = new PathGeometry();
PathFigure pFigure = new PathFigure();
pFigure.StartPoint = new Point(xStart, yStart);
LineSegment lineSegment = new LineSegment(new Point(x, y),true);
pFigure.Segments.Add(lineSegment);
animationPath.Figures.Add(pFigure);
// Create a path to follow
Path path = new Path();
path.Data = animationPath;
path.Stroke = System.Windows.Media.Brushes.Green;
this.bedCanvas.Children.Add(path);
// Freeze the PathGeometry for performance benefits.
animationPath.Freeze();
// Create a MatrixAnimationUsingPath to move the
// button along the path by animating
// its MatrixTransform.
MatrixAnimationUsingPath matrixAnimation =
new MatrixAnimationUsingPath();
matrixAnimation.PathGeometry = animationPath;
double time = GetTimeForVelocityOverPath(animationPath, this.velocityMove);
matrixAnimation.Duration = TimeSpan.FromSeconds(time);
//matrixAnimation.RepeatBehavior = RepeatBehavior.;
// Set the animation's DoesRotateWithTangent property
// to true so that rotates the rectangle in addition
// to moving it.
matrixAnimation.DoesRotateWithTangent = false;
// Set the animation to target the Matrix property
// of the MatrixTransform named "ButtonMatrixTransform".
Storyboard.SetTargetName(matrixAnimation, "MoveCrossHairMatrixTransform");
Storyboard.SetTargetProperty(matrixAnimation,
new PropertyPath(MatrixTransform.MatrixProperty));
// Create a Storyboard to contain and apply the animation.
Storyboard pathAnimationStoryboard = new Storyboard();
pathAnimationStoryboard.Children.Add(matrixAnimation);
return pathAnimationStoryboard;
}
I am trying to do a basic animation using the DoubleAnimation class in WPF. On a button click the rectangle spins. The cs code is
DoubleAnimation da = new DoubleAnimation();
da.From = 0; // start from 0 to 360, full circle
da.To = 360;
da.Duration = new Duration(TimeSpan.FromSeconds(0.5));
da.RepeatBehavior = RepeatBehavior.Forever;
RotateTransform rt = new RotateTransform();
rectangle1.RenderTransform = rt;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
What i am trying to do is to update the da.Duration = new Duration(TimeSpan.FromSeconds(0.5)) value with the slider value.
Tried with slider value binding, but not able to get this correctly.
Changing the Duration is not enough. How fast the animation is controlled by the inner AnimationClock. This clock is initialized just once after you call BeginAnimation(). So you have to update the inner AnimationClock by using ApplyAnimationClock() method.
Also note that you should also set the From property to the currently animated value, so that when changing the Slider's Value, the Angle will be animated smoothly (without restarting from 0):
//The ValueChanged handler for your Slider
private void slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs e){
da.Duration = new Duration(TimeSpan.FromMilliseconds(yourSlider.Value));
da.From = rt.Angle;
rt.ApplyAnimationClock(RotateTransform.AngleProperty, da.CreateClock());
}
Note that I suppose the slider's Value corresponds to the number of milliseconds, so the Maximum value of the Slider should be large enough (about several thousands) if you don't want your Rectangle to be rotated super fast. Also all the variables you declared should be able to access inside the ValueChanged handler.
One more important note is you should use By property here instead of To, like this:
da.By = 360;
That way you can just update the From without caring about the To (it is always 360 degrees rotated).
I have my custom 3D model class (Model) which contains Visual3D element and a Storyboard (sb) to hold animations related to that model. I'm trying to rotate the Visual3D element using the Storyboard but unfortunately it's not working.
Here is the code snippet
public void AnimationRotate(Model model, double duration, double startTime, RepeatBehavior behaviour)
{
//Rotate transform 3D
RotateTransform3D rotateTransform = new RotateTransform3D();
//assign transform to the model
model.Visual3D.Transform = Transform3DHelper.CombineTransform(model.Visual3D.Transform, rotateTransform);
//define the rotation axis
AxisAngleRotation3D rotateAxis = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 180);
//create 3D rotation animation
Rotation3DAnimation rotateAnimation = new Rotation3DAnimation(rotateAxis, TimeSpan.FromSeconds(0.5));
//rotation behaviour
rotateAnimation.RepeatBehavior = behaviour;
//start animation from time
rotateAnimation.BeginTime = TimeSpan.FromSeconds(startTime);
//begin animation - THIS WORKS FINE
// rotateTransform.BeginAnimation(RotateTransform3D.RotationProperty, rotateAnimation);
Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath(RotateTransform3D.RotationProperty));
Storyboard.SetTarget(rotateAnimation, rotateTransform);
//add animation to the storyboard of the model
model.sb.Children.Add(rotateAnimation);
//BUT THIS APPROACH IS NOT WOKRING
model.sb.Begin();
}
The problem is described in this answer.
Instead of using Storyboard.SetTarget you need to register a name for the transform and call Storyboard.SetTargetName. Furthermore you must call Storyboard.Begin(FrameworkElement) and pass a FrameworkElement with the appropriate name scope as parameter (this here).
RegisterName("RotateTransform", rotateTransform);
Storyboard.SetTargetName(rotateAnimation, "RotateTransform");
...
model.sb.Begin(this);
Also I guess you need to clear the Storyboard's Children somewhere, or create a new Storyboard whenever the animation is started.
Im trying to use the TranslateTransform class to move a image on a Grid on Y axis. I need this movment to be smooth so i can not use SetMargin or SetCanvas. I try this in code behind:
public void MoveTo(Image target, double oldY, double newY)
{
var trans = new TranslateTransform();
var anim2 = new DoubleAnimation(0, newY, TimeSpan.FromSeconds(2))
{EasingFunction = new SineEase()};
target.RenderTransform = trans;
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
The object i want to use (a Image control) is placed on a Grid.
For the first time everything works fine.
The problems comes when i try to move the object again using the same function.
The object (a Image control) first move to the start position (initial Y coordinate) then the animation begins.
Is it not suposed for the TranslateTransform to change the coordinates (in my case the Margin property) too?
Thank you.
The transform does not change the original values.they are your point of origin. If you want a new point of origin each time you move you can handle the animation completed event. Or from your transform you can get your current offset and make that your new start point for the animation.
In other words your start values would always be your last move to values
The TranslateTransform is a specific kind of render transformation. Rather that changing properties of the control (such as the Margin property), it simply affects how the control is displayed on the screen.
You have to use the By property of DoubleAnimation.
Try that:
//everytime you execute this anmation your object will be moved 2.0 further
double offset = 2.0
var anim2 = new DoubleAnimation(newY, TimeSpan.FromSeconds(2));
anim2.To = null;
anim2.By = offset;
You've explicitly told the animation to start from 0. It's doing what you've told it.
Just remove the explicit zero fromvalue and everything will work.
var anim2 = new DoubleAnimation(newY, TimeSpan.FromSeconds(2))
{ EasingFunction = new SineEase() };