I created an animation of Dependency Property Canvas.Left with code:
DoubleAnimation myDoubleAnimation = new DoubleAnimation { From =1, To = 100, Duration = new Duration(TimeSpan.FromMilliseconds(500)) };
Storyboard.SetTarget(myDoubleAnimation, Image);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath("(Canvas.Left)"));
Storyboard myMovementStoryboard = new Storyboard();
myMovementStoryboard.Children.Add(myDoubleAnimation);
myMovementStoryboard.Begin();
After the animation, I need to clear it with
UIElement.BeginAnimation(DependencyProperty, AnimationTimeline)
by a null AnimationTimeline.
Otherwise, the Canvas.Left will never change after I modify it by code.
However, the UIElement.BeginAnimation only accepts DependencyProperty but not a PropertyPath.
How can I solve this? Thank you.
You would call
Image.BeginAnimation(Canvas.LeftProperty, null);
The Storyboard is redundant. You could as well call
Image.BeginAnimation(Canvas.LeftProperty, myDoubleAnimation);
You may also avoid that the animation holds the value by setting
myDoubleAnimation = new DoubleAnimation
{
From = 1,
To = 100,
Duration = TimeSpan.FromMilliseconds(500),
FillBehavior = FillBehavior.Stop
};
Related
I want to apply a Blink animation to a Canvas so that all the objects I have drawn on it would blink with it.
I have been somewhat successful using the code below which changes the Opacity property of the Canvas rather fast to achieve this effect but I'm kinda not satisfied with it.
I would prefer a pure blink without any FadeOut/FadeIn as in my current code. How can I do it the right way?
var blinkAnimation = new DoubleAnimation
{
From = 1,
To = 0
};
var blinkStoryboard = new Storyboard
{
Duration = TimeSpan.FromMilliseconds(500),
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = true
};
Storyboard.SetTarget(blinkAnimation, MyCanvas);
Storyboard.SetTargetProperty(blinkAnimation, new PropertyPath(OpacityProperty));
blinkStoryboard.Children.Add(blinkAnimation);
MyCanvas.BeginStoryboard(blinkStoryboard);
Maybe I can do that using the VisibilityProperty but I couldn't get it right.
You might use a second animation with an appropriate BeginTime:
var switchOffAnimation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.Zero
};
var switchOnAnimation = new DoubleAnimation
{
To = 1,
Duration = TimeSpan.Zero,
BeginTime = TimeSpan.FromSeconds(0.5)
};
var blinkStoryboard = new Storyboard
{
Duration = TimeSpan.FromSeconds(1),
RepeatBehavior = RepeatBehavior.Forever
};
Storyboard.SetTarget(switchOffAnimation, MyCanvas);
Storyboard.SetTargetProperty(switchOffAnimation, new PropertyPath(Canvas.OpacityProperty));
blinkStoryboard.Children.Add(switchOffAnimation);
Storyboard.SetTarget(switchOnAnimation, MyCanvas);
Storyboard.SetTargetProperty(switchOnAnimation, new PropertyPath(Canvas.OpacityProperty));
blinkStoryboard.Children.Add(switchOnAnimation);
MyCanvas.BeginStoryboard(blinkStoryboard);
If you want on/off state for your animation you can change your animation to DoubleAnimationUsingKeyFrames
var blinkAnimation = new DoubleAnimationUsingKeyFrames();
blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));
blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(250))));
var blinkStoryboard = new Storyboard
{
Duration = TimeSpan.FromMilliseconds(500),
RepeatBehavior = RepeatBehavior.Forever,
};
Storyboard.SetTarget(blinkAnimation, MyCanvas);
Storyboard.SetTargetProperty(blinkAnimation, new PropertyPath(OpacityProperty));
blinkStoryboard.Children.Add(blinkAnimation);
blinkStoryboard.Begin();
This may be animation related. I have a WPF control whose width and height I'm animating through several different storyboards. I create the storyboards and then call Begin() on them. I've provided code for how the storyboards look below.
I want to re-evaluate the control size on some event (e.g. window resize) so that it is different from the value that was animated. I attempt to handle this on SizeChanged manually (after the animation has run) by setting the Width and Height properties of my control. The debugger shows that those values are not set (the original values remain).
When I inspect the WPF through Snoop, the Width and Height rows are highlighted in a peach/orange color and trying to set the values through it again does not persist it (the original value is shown when I focus away. My guess is that my animation is somehow overriding manual changes to the property but I'm not sure if this is true or how to solve it.
Storyboard Class
public class MyAnimationClass
{
private Storyboard _myStoryboard;
private DoubleAnimation _animation1;
private DoubleAnimation _animation2;
private DoubleAnimation _animation3;
private void InitializeStoryboard()
{
_myStoryboard = CreateMyStoryboard(out _animation1, out _animation2, out _animation3);
}
private Storyboard CreateMyStoryboard(out DoubleAnimation animation1, out DoubleAnimation animation2, out DoubleAnimation animation3)
{
var myStoryboard = new Storyboard();
// create animations
animation1 = new DoubleAnimation { Duration = new TimeSpan(0, 0, 0, 0, 250), From = 0, To = 0 };
animation2 = new DoubleAnimation { BeginTime = new TimeSpan(), Duration = new TimeSpan(0, 0, 0, 0, 250), From = 35, To = 35 };
animation3 = new DoubleAnimation { BeginTime = new TimeSpan(0, 0, 0, 0, 250), Duration = new TimeSpan(0, 0, 0, 0, 250), From = 35, To = 0 };
Storyboard.SetTargetProperty(animation1, new PropertyPath(FrameworkElement.WidthProperty));
Storyboard.SetTargetProperty(animation2, new PropertyPath(FrameworkElement.HeightProperty));
Storyboard.SetTargetProperty(animation3, new PropertyPath(FrameworkElement.HeightProperty));
myStoryboard.Children.Add(animation1);
myStoryboard.Children.Add(animation2);
myStoryboard.Children.Add(animation3);
return myStoryboard;
}
public void Animate(Control controlToAnimate)
{
// ....
var finalWidth = CalculateFinalWidth();
var finalHeight = CalculateFinalHeight();
_animation1.To = finalWidth;
_animation3.To = finalHeight;
_myStoryboard.Begin(controlToAnimate);
}
}
When I want to animate something, I call Animate() on the instance of my MyAnimationClass class.
Thoughts?
This ended up being a pretty simple fix. The value wasn't changing because of FillBehavior controlling the property value. However, simply changing it to FillBehavior.Stop didn't solve my problem because when my non-animation code set width/height, the next time my animation ran it would animate to the width/height I wanted and then default back to the set height. This was solved by setting the width/height of the control after calculation and before animation:
public void Animate(Control controlToAnimate)
{
// ....
var finalWidth = CalculateFinalWidth();
var finalHeight = CalculateFinalHeight();
// set values here so after animation they stay
controlToAnimate.Width = finalWidth;
controlToAnimate.Height = finalHeight;
_animation1.To = finalWidth;
_animation3.To = finalHeight;
_myStoryboard.Begin(controlToAnimate);
}
Im attempting to create a storyboard in C# not XAML to control scaling of an image, so i can easily alter the ScaleTransform.ScaleX and ScaleTransform.ScaleY values in a DoubleAnimation.
So far I believe i have created the animations and added it to a new storyboard, and the appropriate values change in the C# when i check with breakpoints, but it's not actually working.
My C# looks like this:
public void SetStatistics(double[] value)
{
Storyboard sb = new Storyboard();
sb.Duration = new Duration(TimeSpan.FromSeconds(1));
//Wedge Animation X-Axis
DoubleAnimation wax = new DoubleAnimation();
//Wedge Animation Y-Axis
DoubleAnimation way = new DoubleAnimation();
ScaleTransform st = ((ScaleTransform)FindName("wedge1scale"));
wax = new DoubleAnimation();
way = new DoubleAnimation();
wax.Duration = sb.Duration;
way.Duration = sb.Duration;
sb.Children.Add(wax);
sb.Children.Add(way);
Storyboard.SetTargetProperty(wax, new PropertyPath("(ScaleTransform.ScaleX)"));
//End scale from calculation with an Enum value
wax.To = StatMin + (StatPercent * value[1]);
//Start scale from current value
wax.From = st.ScaleX;
Storyboard.SetTargetProperty(way, new PropertyPath("(ScaleTransform.ScaleY)"));
//End scale from calculation with an Enum value
way.To = StatMin + (StatPercent * value[1]);
//Start scale from current value
way.From = st.ScaleY;
Storyboard.SetTarget(wax, Wedge1);
Storyboard.SetTarget(way, Wedge1);
Main.Resources.Add("animation", sb);
sb.Begin();
}
My XAML Image is like this:
<Image x:Name="Wedge1" Source="Images/Wedge.png" RenderTransformOrigin="-0.008,1.027" Height="682" Width="263" Canvas.Left="869.04" Canvas.Top="-158.251" >
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="wedge1scale" ScaleX="0.555" ScaleY="0.555"/>
<TranslateTransform X="88.102" Y="-4.381"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
Thanks in advance for any info :)
The problem is your PropertyPath. You would have to write RenderTransform.Children[0].ScaleX and RenderTransform.Children[0].ScaleY in order to animate the ScaleX and ScaleY properties of the first child of the TransformGroup in the Image's RenderTransform.
var wax = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
var way = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
wax.To = ...
way.To = ...
Storyboard.SetTargetProperty(
wax, new PropertyPath("RenderTransform.Children[0].ScaleX"));
Storyboard.SetTargetProperty(
way, new PropertyPath("RenderTransform.Children[0].ScaleY"));
Storyboard.SetTarget(wax, Wedge1);
Storyboard.SetTarget(way, Wedge1);
var sb = new Storyboard();
sb.Children.Add(wax);
sb.Children.Add(way);
sb.Begin();
And it would be less code without the Storyboard:
var wax = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
var way = new DoubleAnimation { Duration = TimeSpan.FromSeconds(1) };
wax.To = ...
way.To = ...
wedge1scale.BeginAnimation(ScaleTransform.ScaleXProperty, wax);
wedge1scale.BeginAnimation(ScaleTransform.ScaleYProperty, way);
And I guess in your case it isn't necessary to set the From property, as the animations start from the current property values by default.
I have popup and i have a animation using its horizontal and vertical offsets. This Animation takes these offsets as values and have a simple storyboard. For the 1st time it works fine and the second time, the Horizontal offset was not updating the value. I dono why its like this. I have checked the Dp with the property changed handlers, its not get changed. Retains its old value. There is no binding at all, value is set directly.
What might be the reason for this. Any idea on this??
var duration = new TimeSpan(0, 0, 0, 0, 200);
horizontalDoubleAnimation = new EasingDoubleKeyFrame();
verticalDoubleAnimation = new EasingDoubleKeyFrame();
horizontalDoubleAnimation.KeyTime = KeyTime.FromTimeSpan(duration);
verticalDoubleAnimation.KeyTime = KeyTime.FromTimeSpan(duration);
horizontalDoubleAnimation.EasingFunction = new CircleEase();
verticalDoubleAnimation.EasingFunction = new CircleEase();
DoubleAnimationUsingKeyFrames horizontalAnimationUsingKeyFrames = new DoubleAnimationUsingKeyFrames();
DoubleAnimationUsingKeyFrames verticalAnimationUsingKeyFrames = new DoubleAnimationUsingKeyFrames();
horizontalAnimationUsingKeyFrames.KeyFrames.Add(horizontalDoubleAnimation);
verticalAnimationUsingKeyFrames.KeyFrames.Add(verticalDoubleAnimation);
reverseAnimationStoryboard = new Storyboard { Duration = duration };
reverseAnimationStoryboard.Children.Add(horizontalAnimationUsingKeyFrames);
reverseAnimationStoryboard.Children.Add(verticalAnimationUsingKeyFrames);
Storyboard.SetTarget(horizontalAnimationUsingKeyFrames, this.DraggablePopup);
Storyboard.SetTarget(verticalAnimationUsingKeyFrames, this.DraggablePopup);
Storyboard.SetTargetProperty(horizontalAnimationUsingKeyFrames, new PropertyPath("HorizontalOffset"));
Storyboard.SetTargetProperty(verticalAnimationUsingKeyFrames, new PropertyPath("VerticalOffset"));
this.DraggablePopup.Resources.Add("storyboard", reverseAnimationStoryboard);
Animations in WPF provide the boolean parameter AutoReverse. Is it possible to make a call to the functionality implemented by AutoReverse = "true"?
My goal is to spare some troublesome reverse animation and especially spare alot of code. The reverse must not happen directly after the animation ended.
Example of using AutoReverse and True to animate only the reverse animation, but this does not work as required - it still animates the actual animation and the reverse animation.
TranslateTransform transform = new TranslateTransform(0.0, 0.0);
myBox.RenderTransform = transform;
sb = new Storyboard();
Duration dur = new Duration(TimeSpan.FromSeconds(0.5));
DoubleAnimation shiftAnimation = new DoubleAnimation(100.0, dur);
shiftAnimation.AutoReverse = true;
sb.Children.Add(shiftAnimation);
Storyboard.SetTarget(shiftAnimation, myBox);
Storyboard.SetTargetProperty(shiftAnimation, new PropertyPath("RenderTransform.X"));
sb.Seek(TimeSpan.FromSeconds(0.5));
sb.Begin();
you could set the timeclock to 100% of the animationduration so when the animation starts, it will skip the non-reversed part!
Edit:
if Duration is set to 0:0:1.5 you apply a new timeline to your animation set to 0:0:1.5. Autoreverse has to be set to on and then you yust have to start the animation.
Edit 2:
TranslateTransform transform = new TranslateTransform(0.0, 0.0);
myBox.RenderTransform = transform;
sb = new Storyboard();
Duration dur = new Duration(TimeSpan.FromSeconds(0.5));
DoubleAnimation shiftAnimation = new DoubleAnimation(100.0, dur);
shiftAnimation.AutoReverse = true;
sb.Children.Add(shiftAnimation);
Storyboard.SetTarget(shiftAnimation, myBox);
Storyboard.SetTargetProperty(shiftAnimation, new PropertyPath("RenderTransform.X"));
sb.Begin();
sb.Pause();
sb.Seek(sb.Duration.TimeSpan);
sb.Resume();
I have found that the following code works nicely:
private bool reverse=false;
TimeSpan animationDuration = TimeSpan.FromMilliseconds(500);
Storyboard storyboard1 = new Storyboard();
private void prepareStoryBoard(Button btn)
{
btn.RenderTransform = new CompositeTransform();
DoubleAnimation animationShrink = new DoubleAnimation() { To = 0,
Duration =animationDuration , FillBehavior = FillBehavior.HoldEnd };
storyboard1.Children.Add(animationShrink);
Storyboard.SetTarget(animationShrink, btn);
Storyboard.SetTargetProperty(animationShrink,
"(Button.RenderTransform).(CompositeTransform.ScaleX)");
}
private void toggleAnimate()
{
if(reverse==false)
{
storyboard1.AutoReverse = false;
storyboard1.Begin();
reverse=true;
}
else
{
storyboard1.AutoReverse = true;
storyboard1.Seek(animationDuration);
storyboard1.Resume();
reverse = false;
}
}
The first time toggleAnimate() is called, the animation is executed in the normal direction, the second time toggleAnimate() is invoked the animation is reversed.