I'm working on a 3D carousel of flat, square tiles that will contain information. I'm working on animating this carousel to rotate when a person presses Next and Previous buttons.
I've gotten it to work by using BeginAnimation on the Rotation property of the RotateTransform3D I applied to the carousel, but I can't seem to make a Storyboard version of the same animation work. The reason I need the Storyboard version is for the HandOffBehavior.Compose parameter because without it, multiple clicks of my next and previous buttons results in a misaligned carousel.
Here is the code for the Storyboard:
RotateTransform3D tempTransform = (RotateTransform3D)wheel.Transform;
AxisAngleRotation3D rotation = (AxisAngleRotation3D)tempTransform.Rotation;
Storyboard storyboard = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
animation.By = defaultAngle;
animation.Duration = TimeSpan.FromSeconds(.5);
Storyboard.SetTarget(animation, rotation);
Storyboard.SetTargetProperty(animation, new PropertyPath("Angle"));
storyboard.Children.Add(animation);
storyboard.Duration = animation.Duration;
storyboard.Begin(new FrameworkContentElement(), HandoffBehavior.Compose);
For some reason, this code results in absolutely nothing. I followed the examples I had to the letter, so I am quite frustrated. Any help is greatly appreciated. I am also completely open to using BeginAnimation if I can replicate HandOffBehavior.Compose.
My experience comes from 2D animation, but I guess the problem is the same.
For some stupid reason (probably relating to an unhealthy focus on XAML), Storyboards can only animate Freezable objects by looking them up by name. (See example in Storyboards Overview.) Thus although you provide a reference to your 'rotation' object when you call Storyboard.SetTarget(animation, rotation), the Storyboard only wants to remember and use a name, which it does not have.
The solution is:
Create a naming scope around the element that will govern the transform.
Call RegisterName() for each Freezable object being animated.
Pass the element to Storyboard.Begin()
Which would make your code look something like this (not tested):
FrameworkContentElement element = new FrameworkContentElement();
NameScope.SetNameScope(element, new NameScope());
RotateTransform3D tempTransform = (RotateTransform3D)wheel.Transform;
AxisAngleRotation3D rotation = (AxisAngleRotation3D)tempTransform.Rotation;
element.RegisterName("rotation", rotation);
Storyboard storyboard = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
animation.By = defaultAngle;
animation.Duration = TimeSpan.FromSeconds(.5);
Storyboard.SetTarget(animation, rotation);
Storyboard.SetTargetProperty(animation, new PropertyPath("Angle"));
storyboard.Children.Add(animation);
storyboard.Duration = animation.Duration;
storyboard.Begin(element, HandoffBehavior.Compose);
None of this is necessary in XAML because your objects are automatically registered.
EDIT: But then I worked out that you can simplify things by leaving out the Storyboard altogether:
var T = new TranslateTransform(40, 0);
Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 0);
DoubleAnimation anim = new DoubleAnimation(30, duration);
T.BeginAnimation(TranslateTransform.YProperty, anim);
Related
I have a user control that acts like a progress bar and animates the width of a rectangle as a response to an event. Someone arises the event with certain % and the rectangle width animates from its actual width to the % of the actualWidth of the user control.
IF I try to set the new width I get the "The calling thread cannot access this object because a different thread owns it." So I use the Dispatcher.Invoke and it runs nicely.
The problem appears if I try to animate the width change instead of just setting it. Then I get the different thread owns it error event when using the dispatcher.
So. This piece of code works nicely:
bar.Dispatcher.Invoke((Action)delegate { bar.Width = myWidth; });
But this piece of code does not:
DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.From = bar.ActualWidth;
widthAnimation.To = myWidth;
widthAnimation.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 500));
widthAnimation.RepeatBehavior = new RepeatBehavior(1);
bar.Dispatcher.Invoke( (Action)delegate {
bar.BeginAnimation(Rectangle.WidthProperty, widthAnimation);
});
So.. how am I suposed to run an animation on a user control like this one??
Thanks in advance !!!
The animation should also be created in the UI thread:
bar.Dispatcher.Invoke((Action)delegate
{
var widthAnimation = new DoubleAnimation
{
From = bar.ActualWidth,
To = myWidth,
Duration = TimeSpan.FromMilliseconds(500)
};
bar.BeginAnimation(Rectangle.WidthProperty, widthAnimation);
});
I can resize datagrid in the window until I excecute this code:
// Handle the tabcontrol animation
DoubleAnimation dbTbViewsAnimation =
new DoubleAnimation(dToTabHeightParameter, new Duration(new TimeSpan(0,0,1)));
this.tbViews.BeginAnimation(TabControl.HeightProperty, dbTbViewsAnimation);
// Handle the tabcontrol animation
DoubleAnimation dbCurrentPlaylistHeightAnimation =
new DoubleAnimation(dToCurrentPlaylistParameter, new Duration(new TimeSpan(0, 0, 1)));
this.dgCurrentPlaylist.BeginAnimation(DataGrid.HeightProperty, dbCurrentPlaylistHeightAnimation);
What is the problem in this code and why it blocks the datagrid resizing?
thank you!
See Timeline.FillBehavior Property. The default value is HoldEnd. You could use a constructor overload to change this behavior.
DoubleAnimation dbTbViewsAnimation =
new DoubleAnimation(dToTabHeightParameter,
new Duration(new TimeSpan(0,0,1)),
FillBehavior.Stop);
FYI: How to: Set a Property After Animating It with a Storyboard
I'm implementing transitions in a WPF application.
First I "save" my 2 FrameworkElement in 2 ImageBrush.
Then I set the Input & Back (Brush) properties of my shader Effect with them.
CustomEffect s = new CustomEffect();
RenderTargetBitmap rtb = new RenderTargetBitmap((int)SourceFrameWorkElement.ActualWidth, (int)SourceFrameWorkElement.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(SourceFrameWorkElement);
ImageBrush ib = new ImageBrush(rtb);
s.Input = ib;
rtb.Render(TargetFrameWorkElement);
ib.ImageSource = rtb;
s.Back = ib;
SourceFrameWorkElement.Effect = s;
Now that all is set up, I want to animate the Time property of my shader, and i've tried this:
DoubleAnimation refDoubleAnimation = new DoubleAnimation(0.0, 1.0, Duration);
Storyboard.SetTarget(refDoubleAnimation, SourceFrameWorkElement);
Storyboard.SetTargetProperty(refDoubleAnimation, new PropertyPath("(Effect).(CustomEffect.Time)");
refStoryboard.Children.Add(refDoubleAnimation);
refStoryboard.Completed += new EventHandler(OnStoryboardCompleted);
refStoryboard.Begin(SourceFrameWorkElement, true);
and i get an InvalidOperationException on the begin method with this message:
"Cannot resolve all property references in the property path '(Effect).(CustomEffect.Time)'.
Verify that applicable objects supports the properties."
But when I use a built in Effect like BlurEffect, it works....
Can someone tell me where i'm wrong ?
Edit:
I've also tried
SourceElement.Effect.BeginAnimation(SlideInEffect.TimeProperty, refDoubleAnimation)
instead of using the storyboard, I don't get an exception but the second image pop instantly and the animation is not playing
The solution was to use BeginAnimation ^^
In fact, I had the second image with opacity to 1, and the animation was playing behind
(i checked if the time elapsed to get in my OnAnimationCompleted eventHandler matched with the transition Duration)
so i've created a second animation on the TargetElement opacity with 2 DiscreteDoubleKeyFrames to do the trick and now it works ^^
Maybe the Storyboard thing could work if i add the namespace in the PropertyPath but i have no time to test it so give it a try if you want, and update the post ^^.
I want to add animation to the loading user controls in a StackPanel.
So I add these lines to the existing project :
control.Loaded += UserControlLoaded;
and
public void UserControlLoaded(object sender, System.Windows.RoutedEventArgs e)
{
UserControl control = (UserControl)sender;
DoubleAnimation fadeInAnimation = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(5)));
Storyboard.SetTarget(fadeInAnimation, control);
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
Storyboard sb = new Storyboard();
sb.Children.Add(fadeInAnimation);
sb.Begin();
}
It works good but I want to change it to scale the user control in both axis from 0 to 1 within 2 seconds but I can't find the code to set target property of the story board to the LayoutTransform X and Y axis !
The transform was made in MS Blend by this way :
How can I do it programmatically.
Thanks in advance for your kind attention.
The code that would animate the control's LayoutTransform depends on the kind of Transform used. Provided that it simply is a ScaleTransform, you could write this:
FrameworkElement control = sender as FrameworkElement;
ScaleTransform transform = control.LayoutTransform as ScaleTransform;
DoubleAnimation scaleAnimation =
new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(2)));
transform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation);
transform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation);
When the LayoutTransform was created with Blend, it is most certainly not simply a ScaleTransform, but a TransformGroup with a ScaleTransform as first child. You would then retrieve the ScaleTranform by something like this:
TransformGroup transformGroup = control.LayoutTransform as TransformGroup;
ScaleTransform transform = transformGroup.Children[0] as ScaleTransform;
And again i forgot the reason why animating a Transform like this won't work by means of a Storyboard. Instead of directly calling BeginAnimation on the ScaleTransform object, I've tried the code below, but without success.
DoubleAnimation xScaleAnimation =
new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(2)));
Storyboard.SetTarget(xScaleAnimation, transform);
Storyboard.SetTargetProperty(xScaleAnimation,
new PropertyPath(ScaleTransform.ScaleXProperty));
DoubleAnimation yScaleAnimation =
new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(2)));
Storyboard.SetTarget(yScaleAnimation, transform);
Storyboard.SetTargetProperty(yScaleAnimation,
new PropertyPath(ScaleTransform.ScaleYProperty));
Storyboard sb = new Storyboard();
sb.Children.Add(xScaleAnimation);
sb.Children.Add(yScaleAnimation);
sb.Begin();
Greetings!
I am currently working on a Silverlight project and I would like to animate a simple polygon shape (a trapezoid actually). Specifically, I woudld like to move two of the four points dynamically after some event happened. I need / want to resize and move one of the parallel sides to another position.
I admit I am rather new to Silverlight and have not found a source that could tell me wether it is even possible, not to mention how it can be done.
I have used animations before, so the general concept of storyboards and animations is not new to me. But how can I move the points of a polygon in an animation? Are there alternatives that have a similar optical effect (e.g. animating a path)?
Is there a PropertyPath I can use, similar to
P3AnimBack.SetValue(Storyboard.TargetPropertyProperty,
new PropertyPath("(Path.Data).
(PathGeometry.Figures)[0].(PathFigure.Segments)[0].
(BezierSegment.Point3)"));
, as found in a Point Animation in Silverlight 3 tutorial?
Thank you all in advance. :)
i don't know anything about Silverlight, or animations in .NET in general, but Charles Petzold did something similar:
Squaring the Circle (view animation in browser)
Animating Points and PointCollections in Silverlight (view in browser)
Animated Polyline Interpolations in Silverlight
As requested in a comment, I explain what I finally used to make my animation:
I follwed up on Animated Polyline Interpolations in Silverlight and more or less directly used this code - "stealing" the PointCollectionInterpolator.cs class.
Then I had my method to create the polygons I need and prepare animations:
private void CreatePolygon(TextBox txtbx, string prop, Color curcol)
{
PointCollectionInterpolator pci = new PointCollectionInterpolator();
pci.Points1 = new PointCollection() // Start Points
{
new Point(...),
new Point(...),
new Point(...),
new Point(...),
};
pci.Points2 = new PointCollection() // End Points
{
new Point(...),
new Point(...),
new Point(...),
new Point(...),
};
Polygon tmpply = new Polygon();
LayoutRoot.Children.Add(tmpply);
tmpply.Points = pci.InterpolatedPoints;
DoubleAnimation animpci = new DoubleAnimation();
animpci.Duration = someDuration;
animpci.From = 0.0;
animpci.To = 1.0;
Storyboard.SetTarget(animpci, pci);
Storyboard.SetTargetProperty(animpci, new PropertyPath("(Progress)"));
myStoryBoard.Children.Add(animpci);
}
And then in some random event handler, I start the animation. Additionally, so I can reuse the method, I moved the end points collection into the start points collection and update the interpolator with new end points. (Remember setting the progress to 0.0 ...) So each time the handler fires, the polygon seamlessly morphs into a new one.
private void SomeEventHandler(object sender, RoutedEventArgs e)
{
PointCollectionInterpolator polygonPCI =
this.referenceToPointCollectionInterpolator;
polygonPCI.Points1 = polygonPCI.Points2;
polygonPCI.Progress = 0.0;
polygonPCI.Points2 = getNewEndPoints();
myStoryBoard.Begin();
}
In retrospect, i would change the names from Points1 and Points2 to StartPoints and EndPoints resp.
Hope this helped. :)