Get DoubleAnimation Reference Object from BeginAnimation - wpf

I want to add a simple double animation with code-behind to apply a fadein to n objects created at runtime:
foreach (var rect in howmanyrect) {
Rectangle bbox = new Rectangle {
Width = rect.Width,
Height = rect.Height,
Stroke = Brushes.BlueViolet,
Opacity = 0D
};
DoubleAnimation da = new DoubleAnimation {
From = 0D,
To = 1D,
Duration = TimeSpan.FromMilliseconds(500D),
RepeatBehavior = new RepeatBehavior(1),
AutoReverse = false
};
GridContainer.Children.Add(bbox);
Canvas.SetLeft(bbox, rect.Left);
Canvas.SetTop(bbox, rect.Top);
bbox.Tag = da; // <- Look HERE
bbox.BeginAnimation(OpacityProperty, da);
After this, when requested, delete the objects collection with fadeout:
foreach (var child in GridContainer.Children) {
Rectangle bbox = (Rectangle) child;
DoubleAnimation da = (DoubleAnimation) bbox.Tag; // <- Look HERE
da.From = 1D;
da.To = 0D;
var childCopy = child; // This copy grants the object reference access for removing inside a forech statement
da.Completed += (obj, arg) => viewerGrid.Children.Remove((UIElement) childCopy);
bbox.BeginAnimation(OpacityProperty, da);
}
This code works perfectly but it's a workaround. In my first revision I created a new Doubleanimation object in the delete method but when I started the animation every object excetutes the first and the second animation before being deleted.
So I decide to pass a reference to the DoubleAnimation instance with the Tag property and change the animation properties.
Is there another way to obtain a reference to the DoubleAnimation object attached with BeginAnimation or to avoid the first animation to be repeated?
Thanks
Lox

Related

Add event handler (MouseDown) dynamically for PathFigure C# WPF

I created an object from points with this code, dynamically:
SolidColorBrush brushColor = (SolidColorBrush)new BrushConverter().ConvertFromString(_brushColor);
PathFigure figures = new PathFigure();
figures.StartPoint = points[0];
points.RemoveAt(0);
figures.Segments = new PathSegmentCollection(points.Select((p, i) => new LineSegment(p, i % 2 == 0)));
PathGeometry pg = new PathGeometry();
pg.Figures.Add(figures);
canvas.Children.Add(new Path { Stroke = brushColor, StrokeThickness = 3, Data = pg });
Now I want to add event handler for this object. Would not be a problem if object is a path or polyline type. I would just add event handler like this:
poly.MouseDown += new MouseButtonEventHandler(poly_MouseDown);
void poly_MouseDown(object sender, MouseButtonEventArgs e)
{
//code
}
Problem is that I have to use Figures and PathGeometry which does not accept MouseDown event handlers. Since they are from System.Windows.Media class and Path/Polyline is from System.Windows.Shapes I can't find a solution to assign right event handler (MouseDown) to my Figure object.
What is the solution or is there any nice workaround solution for this problem? Maybe cast or convert it somehow?
As I see it you're skipping a crucial step.
Declare the Path Object first, give it the event, and then insert it into your Canvas:
SolidColorBrush brushColor = (SolidColorBrush)new BrushConverter ().ConvertFromString (_brushColor);
PathFigure figures = new PathFigure ();
figures.StartPoint = points[0];
points.RemoveAt (0);
figures.Segments = new PathSegmentCollection (points.Select ((p, i) => new LineSegment (p, i % 2 == 0)));
PathGeometry pg = new PathGeometry ();
pg.Figures.Add (figures);
Path pgObject = new Path({ Stroke = brushColor, StrokeThickness = 3, Data = pg });
pgObject.MouseDown+=new MouseButtonEventHandler(poly_MouseDown);
canvas.Children.Add (pgObject);

WPF Animating a Run element to flash

This is kind of a weird problem I am having right now. What I am trying to do is animate a Run element to essentially flash/blink. The parent is a Hyperlink which contains multiple Inlines of type Run and Image. Now I am trying to animate the Foreground color of the element but it does not seem to work.
Here is my code for a hyperlink.
CallbackHyperLink callbackLink = new CallbackHyperLink();
ToolTipService.SetShowDuration(callbackLink, 3600000);
ToolTipService.SetInitialShowDelay(callbackLink, 0);
callbackLink.Foreground = new SolidColorBrush(Colors.Magenta); // Default text color of the link
callbackLink.TextDecorations = null; // Disable the underline until mouse over
callbackLink.ToolTip = f.Tooltip; // Set the tooltip string
DoubleAnimation opacityAnim = new DoubleAnimation();
opacityAnim.From = 1.0;
opacityAnim.To = 0.0;
opacityAnim.FillBehavior = FillBehavior.Stop;
opacityAnim.Duration = TimeSpan.FromSeconds(BlinkDurationOff);
opacityAnim.AutoReverse = true;
_blinkAnimation.Children.Add(opacityAnim);
Storyboard.SetTarget(opacityAnim, callbackLink.Foreground);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath(SolidColorBrush.OpacityProperty));
_blinkAnimation.Stop();
_blinkAnimation.Begin();
So that gets put in a storyboard which get fired. However the foreground is not getting animated and I am not seeing any warnings that im trying to animate something I shouldn't. Anybody have any ideas?
Thanks
This works:
Storyboard.SetTarget(opacityAnim, callbackLink);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath(UIElement.OpacityProperty));
Edit full working example with a TextBlock stolen from here :
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
TextBlock callbackLink = new TextBlock();
callbackLink.HorizontalAlignment = HorizontalAlignment.Center;
callbackLink.VerticalAlignment = VerticalAlignment.Center;
callbackLink.Text = "Test";
this.Content = callbackLink;
NameScope.SetNameScope(this, new NameScope());
var b = new SolidColorBrush(Colors.Magenta);
callbackLink.Foreground = b;
this.RegisterName("MyAnimatedBrush", b);
DoubleAnimation opacityAnimation = new DoubleAnimation();
opacityAnimation.To = 0.0;
opacityAnimation.Duration = TimeSpan.FromSeconds(0.5);
opacityAnimation.AutoReverse = true;
opacityAnimation.RepeatBehavior = RepeatBehavior.Forever;
Storyboard.SetTargetName(opacityAnimation, "MyAnimatedBrush");
Storyboard.SetTargetProperty(
opacityAnimation, new PropertyPath(SolidColorBrush.OpacityProperty));
Storyboard mouseLeftButtonDownStoryboard = new Storyboard();
mouseLeftButtonDownStoryboard.Children.Add(opacityAnimation);
callbackLink.MouseEnter += delegate(object sender2, MouseEventArgs ee)
{
mouseLeftButtonDownStoryboard.Begin(this, true);
};
callbackLink.MouseLeave += delegate(object sender2, MouseEventArgs ee)
{
mouseLeftButtonDownStoryboard.Stop(this);
};
}
Also another way to link to the foreground opacity call without registering the name of the brush is the following.
Storyboard.SetTarget(opacityAnim, callbackLink);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath("Foreground.Opacity"));
This seems to finally work for me. Still tweaking my solution as I am doing a lot of Stop/Begin calls on the animation which I believe is not good way of doing things.

How can I slide a button/textbox or any control in WPF?

I want to simply animate a text-box such that it fades in and also moves to the left (or any x/y position). How can I achieve that?
Also will it matter if it's inside a Grid?
Here's a sketchy method i just wrote for fading in any kind of UIElement:
public static void FadeIn(UIElement element, int xOffset, TimeSpan duration)
{
Transform tempTrans = element.RenderTransform;
TranslateTransform trans = new TranslateTransform(xOffset, 0);
TransformGroup group = new TransformGroup();
if (tempTrans != null) group.Children.Add(tempTrans);
group.Children.Add(trans);
DoubleAnimation animTranslate = new DoubleAnimation(0, (Duration)duration);
animTranslate.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
DoubleAnimation animFadeIn = new DoubleAnimation(0, 1, (Duration)duration) { FillBehavior = FillBehavior.Stop };
animTranslate.Completed += delegate
{
element.RenderTransform = tempTrans;
};
element.RenderTransform = trans;
element.BeginAnimation(UIElement.OpacityProperty, animFadeIn);
trans.BeginAnimation(TranslateTransform.XProperty, animTranslate);
}
If some of the workings are not clear feel free to ask.

WP7 Multiple controls animation problem

I am storing a set of controls in an array and i am trying to animate all the controls one by one in a loop but I can see only last one animating ?
for (int i = 0; i < 4; i++)
{
Dispatcher.BeginInvoke(() =>
{
var sb = new Storyboard();
sb = CreateStoryboard(1.0, 0.0, this.Lights[0, i]);
sb.Begin();
});
}
private Storyboard CreateStoryboard(double from, double to, DependencyObject targetControl)
{
Storyboard result = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
animation.From = from;
animation.To = to;
animation.Duration = TimeSpan.FromSeconds(1);
animation.BeginTime = TimeSpan.FromSeconds(1);
animation.AutoReverse = false;
Storyboard.SetTarget(animation, targetControl);
Storyboard.SetTargetProperty(animation, new PropertyPath(UIElement.OpacityProperty));
result.Children.Add(animation);
return result;
}
I'm at a loss to explain that behaviour. Without the Dispatcher.BeginInvoke you would just get all items fading at the same time. However I can't see why you wouldn't get the same when using BeginInvoke. Still neither is what you are after. You need to sequence the animations one after another.
Probably the best way to do this is to use a single StoryBoard with multiple animations, the sequencing of animations is afterall the whole point of a Storyboard.
private DoubleAnimation CreateAnimation(double from, double to, DependencyObject targetControl, int index)
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = from;
animation.To = to;
animation.Duration = TimeSpan.FromSeconds(1);
animation.BeginTime = TimeSpan.FromSeconds(1 * index);
animation.AutoReverse = false;
Storyboard.SetTarget(animation, targetControl);
Storyboard.SetTargetProperty(animation, new PropertyPath(UIElement.OpacityProperty));
return animation;
}
Note the extra index parameter and that is use to specify when the animation should begin.
Now your code is simply:-
var sb = new Storyboard();
for (int i = 0; i < 4; i++)
{
sb.Children.Add(CreateAnimation(1.0, 0.0, this.Lights[0, i], i);
}
sb.Begin();

Animate Canvas.Left property

I'm trying to animate the left property of my image that is in a canvas.
When I'm doing :
image.SetValue(Canvas.LeftProperty, destX[i]);
this is working.
But when I'm doing :
Animate(image, lastValue, destX[i], 500);
with
private void Animate(Image image, double val1, double val2, double miliseconds)
{
DoubleAnimation myDoubleAnimation = new DoubleAnimation { From = val1, To = val2, Duration = new Duration(TimeSpan.FromMilliseconds(miliseconds)) };
TranslateTransform ts = new TranslateTransform();
image.RenderTransform = ts;
Storyboard.SetTarget(myDoubleAnimation, ts);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(TranslateTransform.XProperty));
Storyboard myMovementStoryboard = new Storyboard();
myMovementStoryboard.Children.Add(myDoubleAnimation);
myMovementStoryboard.Begin();
myMovementStoryboard.Completed += (s, e) =>
{
image.SetValue(Canvas.LeftProperty, val2);
};
}
It's not working
What can be wrong in my Animate function ?
Even if animation can be bad done, the completed event should reset the good value to canvas.leftproperty.
But in my case, something gets wrong.
How would you have done the Animate function ?
Thanks in advance for any help
I did it this way, and it works fine.
DoubleAnimation myDoubleAnimation = new DoubleAnimation { From = val1, To = val2, Duration = new Duration(TimeSpan.FromMilliseconds(miliseconds)) };
//set the target of the animation
Storyboard.SetTarget(myDoubleAnimation, Image);
//set the target property of the animation. Don't forget the ( ) around canvas.left
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath("(Canvas.Left)"));
//create the storyboard
Storyboard myMovementStoryboard = new Storyboard();
//add the animation to the storyboard
myMovementStoryboard.Children.Add(myDoubleAnimation);
//begin animation
myMovementStoryboard.Begin();
You're setting Canvas.LeftProperty, but you're animating TranslateTransform.XProperty. These are very different properties, as the translate transform is applied after the layout pass. So the object is laid out in the Canvas using Canvas.Left, but then the TranslateTransform causes it to be drawn shifted from that original origin. What you should do is set the TranslateTransform.XProperty back to val2.
myMovementStoryboard.Completed += (s, e) =>
{
ts.X = val2;
};
That said, the animation should not really fail in the middle, so this extra safeguard should not be necessary.

Resources