Event after double animation whilst keeping context - c#

I am trying to create an animation but am having problems executing some code after the animation has occured. The animation code is as follows...
public static void Friend(Canvas canvas)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1)),
AutoReverse = true
};
element.BeginAnimation(Canvas.BottomProperty, slideDown);
slideDown.Completed += (sender, e) => Test(sender, e, element, canvas);
}
}
}
The event to happen afterwards is...
var random = new Random();
element.Source = Core.StreamImage(Wardrobe.Friends[Wardrobe.RandomFriend()]);
Canvas.SetLeft(element, random.Next(0, (int)(canvas.ActualWidth - element.Width)));
// then reverse the previous animation.
As you can see, the event needs to keep the context of the animation in order for it to work but event handlers don't allow this. I have tried adding this code directly underneath the element.BeginAnimation but it is executed prematurely.
I have also tried splitting the parts of the animation up into seperate functions but of course you can't have multiple animations on one object as the functions don't get executed in order resulting in only the last section being played.

Here is the code i used to fix the problem i was having...
public static void Friend(Canvas canvas)
{
var random = new Random();
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1))
};
slideDown.Completed += (sender, e) =>
{
element.Source = Core.StreamImage(Wardrobe.Friends[Wardrobe.RandomFriend()]);
Canvas.SetLeft(element, random.Next(0, (int)(canvas.ActualWidth - element.Width)));
var slideUp = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) + element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1))
};
element.BeginAnimation(Canvas.BottomProperty, slideUp);
};
element.BeginAnimation(Canvas.BottomProperty, slideDown);
}
}
}

Try moving the event above the execution
public static void Friend(Canvas canvas)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1)),
AutoReverse = true
};
slideDown.Completed += (sender, e) => Test(sender, e, element, canvas);
element.BeginAnimation(Canvas.BottomProperty, slideDown);
}
}
}
I'm not 100% but I think the Animation becomes Freezable after it executes, so we cant modify after
BTW, you can have multiple animations on a object but you need to use "StoryBoard".
Storyboard example:
var slideDown = new DoubleAnimation { From = 100, To = 200, Duration = new Duration(TimeSpan.FromSeconds(5)), AutoReverse = true };
var slideLeft = new DoubleAnimation { From = 100, To = 200, BeginTime = TimeSpan.FromSeconds(5), Duration = new Duration(TimeSpan.FromSeconds(5)), AutoReverse = true };
Storyboard storyBoard = new Storyboard();
storyBoard.Children.Add(slideDown);
storyBoard.Children.Add(slideLeft);
Storyboard.SetTargetName(slideDown, myCanvas.Name);
Storyboard.SetTargetName(slideLeft, myCanvas.Name);
Storyboard.SetTargetProperty(slideLeft, new PropertyPath(Canvas.LeftProperty));
Storyboard.SetTargetProperty(slideDown, new PropertyPath(Canvas.TopProperty));
myCanvas.BeginStoryboard(storyBoard);

Related

WPF MediaElement Opacity is not set after executing Storyline

I'm using DoubleAnimation and Storyboard to control Opacity of MediaElement. The animation itself works fine, but if I call Disappear and playVid after several seconds, the Opacity of the player remains 0! What's the problem?
public void playVid(string source, bool isMainVid)
{
player.Opacity = 1;
player.Play(); //player.Opacity is 0 here!
}
public void Disappear()
{
DoubleAnimation fadeOut = new DoubleAnimation
{
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(1000))
};
fadeOut.Completed += (s, e) =>
{
player.Stop();
};
var storyboard = new Storyboard();
storyboard.Children.Add(fadeOut);
Storyboard.SetTargetName(fadeOut, player.Name);
Storyboard.SetTargetProperty(fadeOut, new PropertyPath(OpacityProperty));
storyboard.Begin(mainGrid, HandoffBehavior.SnapshotAndReplace); //mainGrid is player's parent
}
Use FillBehavior equal to Stop, but also set the Opacity of your player to the final opacity value (in the Completed handler). Otherwise, it will be reset to its value before the animation.
var fadeOut = new DoubleAnimation
{
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(1000)),
FillBehavior = FillBehavior.Stop
};
fadeOut.Completed += (s, e) =>
{
player.Stop();
player.Opacity = 0;
};
See this post for other approaches.

Apply transformation on an image on C# for a Windows Phone application

I'm working on a mini-game where I need to make images go from their initial position to another using a translation transformation.
The problem is: I don't know how to proceed to apply the translation.
So here's the code used to generate my images.
Image myImage1 = new Image();
myImage1.Source = new BitmapImage(new Uri("/Images/image1.png", UriKind.Relative));
myImage1.Name = "image" + index++.ToString();
myImage1.Tap += myImage_Tap;
Canvas.SetLeft(image, 200);
Canvas.SetTop(image, 600);
gameArea.Children.Add(image);
Thanks for your time.
You have two choices to move your image around. The first is using a Canvas like your code shows, but you have to make sure your element is actually within a Canvas. Is your "gameArea" a Canvas? If it's not, your code won't work. The other option is to use Transforms.
var myImage1 = new Image
{
Source = new BitmapImage(new Uri("/Images/image1.png", UriKind.Relative)),
Name = "image" + index++.ToString(),
Tap += myImage_Tap,
RenderTransform = new TranslateTransform
{
X = 200,
Y = 600
}
};
gameArea.Children.Add(image);
Now gameArea can be any type of Panel and it will work.
Note that when using a Canvas, your Top and Left will be from the upper left corner of the Canvas. When using a Transform, your X and Y will be relative to where the element would have originally be drawn.
UPDATE -- Helper class to do simple animations using a Canvas
public sealed class ElementAnimator
{
private readonly UIElement _element;
public ElementAnimator(UIElement element)
{
if (null == element)
{
throw new ArgumentNullException("element", "Element can't be null.");
}
_element = element;
}
public void AnimateToPoint(Point point, int durationInMilliseconds = 300)
{
var duration = new Duration(TimeSpan.FromMilliseconds(durationInMilliseconds));
var easing = new BackEase
{
Amplitude = .3
};
var sb = new Storyboard
{
Duration = duration
};
var animateLeft = new DoubleAnimation
{
From = Canvas.GetLeft(_element),
To = point.X,
Duration = duration,
EasingFunction = easing,
};
var animateTop = new DoubleAnimation
{
From = Canvas.GetTop(_element),
To = point.Y,
Duration = duration,
EasingFunction = easing,
};
Storyboard.SetTargetProperty(animateLeft, "(Canvas.Left)");
Storyboard.SetTarget(animateLeft, _element);
Storyboard.SetTargetProperty(animateTop, "(Canvas.Top)");
Storyboard.SetTarget(animateTop, _element);
sb.Children.Add(animateLeft);
sb.Children.Add(animateTop);
sb.Begin();
}
}
So here's what I did with your code, I took just this part and made it a function:
public void AnimateToPoint(UIElement image, Point point, int durationInMilliseconds = 300)
{
var duration = new Duration(TimeSpan.FromMilliseconds(durationInMilliseconds));
var sb = new Storyboard
{
Duration = duration
};
var animateTop = new DoubleAnimation
{
From = Canvas.GetTop(image),
To = point.Y,
Duration = duration
};
Storyboard.SetTargetProperty(animateTop, new PropertyPath("Canvas.Top")); // You can see that I made some change here because I had an error with what you gave me
Storyboard.SetTarget(animateTop, image);
sb.Children.Add(animateTop);
sb.Begin();
}
And then I made the call with:
Point myPoint = new Point(leftpos, 300); // leftpos is a variable generated randomly
AnimateToPoint(myImage, myPoint);
So now there is still a single error, and it's in the instruction "sb.Begin;".
And it says: Cannot resolve TargetProperty Canvas.Top on specified object.
I reaylly don't know how to proceed.
Thanks for your previous answer!

WPF Storyboard and update lable content issues

I have a story board that help me to perform a animation between 2 or more Canvas objects collection. In one of this canvas I have a Label that perform a simple count down update - who do not work.
Wen I truing to update THIS Label Content, after I perform a switch between 2 Canvas, the console give me that warning :
System.Windows.Media.Animation Warning: 6 : Unable to perform action
because the specified Storyboard was never applied to this object for
interactive control.; Action='Stop';
Storyboard='System.Windows.Media.Animation.Storyboard';
Storyboard.HashCode='12309819';
Storyboard.Type='System.Windows.Media.Animation.Storyboard';
TargetElement='MyTest.MainWindow'; TargetElement.HashCode='17070976';
TargetElement.Type='MyTest.MainWindow'
The code example of my Switcher for Canvas object is :
public Storyboard _storyboard = new Storyboard();
const bool ToLeft = false;
const bool ToRight = true;
void ScreenSelector(FrameworkElement currentElement, FrameworkElement toNextElement, bool side)
{
if (_storyboard != null) _storyboard.Stop(this);
var ticknessLeft = new Thickness(Width, 0, -Width, 0);
var ticknessRight = new Thickness(-Width, 0, Width, 0);
var ticknessClient = new Thickness(0, 0, 0, 0);
var timeSpanStarting = TimeSpan.FromSeconds(0);
var timeSpanStopping = TimeSpan.FromSeconds(0.5);
var keyTimeStarting = KeyTime.FromTimeSpan(timeSpanStarting);
var keyTimeStopping = KeyTime.FromTimeSpan(timeSpanStopping);
toNextElement.Margin = side ? ticknessRight : ticknessLeft;
toNextElement.Visibility = Visibility.Visible;
var storyboardTemp = new Storyboard();
var currentThicknessAnimationUsingKeyFrames = new ThicknessAnimationUsingKeyFrames {BeginTime = timeSpanStarting};
Storyboard.SetTargetName(currentThicknessAnimationUsingKeyFrames, currentElement.Name);
Storyboard.SetTargetProperty(currentThicknessAnimationUsingKeyFrames, new PropertyPath("(FrameworkElement.Margin)"));
currentThicknessAnimationUsingKeyFrames.KeyFrames.Add(new SplineThicknessKeyFrame(ticknessClient, keyTimeStarting));
currentThicknessAnimationUsingKeyFrames.KeyFrames.Add(new SplineThicknessKeyFrame(side ? ticknessLeft : ticknessRight, keyTimeStopping));
storyboardTemp.Children.Add(currentThicknessAnimationUsingKeyFrames);
var nextThicknessAnimationUsingKeyFrames = new ThicknessAnimationUsingKeyFrames {BeginTime = timeSpanStarting};
Storyboard.SetTargetName(nextThicknessAnimationUsingKeyFrames, toNextElement.Name);
Storyboard.SetTargetProperty(nextThicknessAnimationUsingKeyFrames, new PropertyPath("(FrameworkElement.Margin)"));
nextThicknessAnimationUsingKeyFrames.KeyFrames.Add(new SplineThicknessKeyFrame(side ? ticknessRight : ticknessLeft, keyTimeStarting));
nextThicknessAnimationUsingKeyFrames.KeyFrames.Add(new SplineThicknessKeyFrame(ticknessClient, keyTimeStopping));
storyboardTemp.Children.Add(nextThicknessAnimationUsingKeyFrames);
storyboardTemp.Completed += (EventHandler)delegate
{
currentElement.Visibility = Visibility.Hidden;
_storyboard = null;
};
_storyboard = storyboardTemp;
BeginStoryboard(storyboardTemp);
}
Now I have a MainWindow_KeyDown event where I check the button pressed :
switch (e.Key)
{
case Key.A:
{
ScreenSelector(fr_GameGrid,Layer_1, ToLeft);
GameEvents gm = new GameEvents();
gm.OnTimer();
}
break;
case Key.B:
{
ScreenSelector(Layer_1, fr_GameGrid, ToRight);
}
break;
I perform the "screen selector" - and start the simple count down timer. In GameEvent I have that simple code :
public static void Countdown(int count, TimeSpan interval, Action<int> ts)
{
var dt = new DispatcherTimer { Interval = interval };
dt.Tick += (_, a) =>
{
if (count-- == 0)
dt.Stop();
else
ts(count);
};
ts(count);
dt.Start();
}
and the Using :
public void OnTimer()
{
/*
* VALIDATE TIME
*
*
* > OnAnswer
*/
Countdown(3, TimeSpan.FromSeconds(1), cur => lbTime.Content = cur.ToString(CultureInfo.InvariantCulture));
}
Can anyone tell me where did I do wrong?, can't start the Label animation, and this is very strange - Because wen I update my Storyboard on line 6 -> this ( if (_storyboard != null) _storyboard.Stop(this); ) to ( if (_storyboard != null) _storyboard.Begin(this,true); ) the Warning disappears.

Create a Blink animation in WPF in code behind

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();

Cannot set Opacity property on ScatterViewItem after an animation has been performed

I am currently removing an element from the user interface by fading it out. This works as expected.
public void HideShape()
{
if (this.TangibleShape != null)
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = 1.0;
animation.To = 0.0;
animation.AutoReverse = false;
animation.Duration = TimeSpan.FromSeconds(1.5);
Storyboard s = new Storyboard();
s.Children.Add(animation);
Storyboard.SetTarget(animation, this.TangibleShape.Shape);
Storyboard.SetTargetProperty(animation, new PropertyPath(ScatterViewItem.OpacityProperty));
s.Begin(this.TangibleShape.Shape);
s.Completed += delegate(object sender, EventArgs e)
{
// call UIElementManager to finally hide the element
UIElementManager.GetInstance().Hide(this.TangibleShape);
};
}
}
The problem is that I want to set the opacity to 1 again in some cases but the TangibleShape.Shape (it's a ScatterViewItem) ignores the command. If I fade out again, the element becomes visible and immediately starts fading out. I don't know how to fix this problem. Does anyone have a hint for me?
public void HideShape()
{
if (this.TangibleShape != null)
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = 1.0;
animation.To = 0.0;
animation.AutoReverse = false;
animation.Duration = TimeSpan.FromSeconds(1.5);
animation.FillBehavior = FillBehavior.Stop; // needed
Storyboard s = new Storyboard();
s.Children.Add(animation);
Storyboard.SetTarget(animation, this.TangibleShape.Shape);
Storyboard.SetTargetProperty(animation, new PropertyPath(ScatterViewItem.OpacityProperty));
s.Completed += delegate(object sender, EventArgs e)
{
// call UIElementManager to finally hide the element
UIElementManager.GetInstance().Hide(this.TangibleShape);
this.TangibleShape.Shape.Opacity = 0.0; // otherwise Opacity will be reset to 1
};
s.Begin(this.TangibleShape.Shape); // moved to the end
}
}
Answer found here: http://social.msdn.microsoft.com/Forums/en-US/7d33ca82-2c02-4004-8b37-47edf4cca34e/scatterviewitem-and-

Categories