WPF: Prevent "Completed" event from firing after removing animation
How can I remove a running animation from a WPF element in such a way that its Completed event does not fire?
The solutions presented here and here remove the animation's visible effects, but the Completed event still fires at the time that the ani开发者_C百科mation would have completed.
Here's some code that demonstrates my problem (it's in the code behind of a Window with a Label, a Button, and a TextBox):
int _count = 0;
private void button1_Click(object sender, RoutedEventArgs e) {
myLabel.Content = "Starting animation: " + _count++;
// Cancel any already-running animations and return
// the label opacity to 1.0.
myLabel.BeginAnimation(Label.OpacityProperty, null);
myLabel.Opacity = 1.0;
// Create a new animation to fade out the label.
DoubleAnimation opacityAnim = new DoubleAnimation(1.0, 0.0, TimeSpan.FromSeconds(2), FillBehavior.Stop) {
BeginTime = TimeSpan.FromSeconds(3)
};
opacityAnim.Completed += (sndr, ev) => {
myTextbox.Text += Environment.NewLine + "Completed fired.";
};
// Start the countdown/animation.
myLabel.BeginAnimation(Label.OpacityProperty, opacityAnim);
}
How can I remove an animation such that it will not raise its Completed event?
Unsubscribe from Completed event... That also means you have to rewrite Completed event handler from lambda to explicit method:
DoubleAnimation _opacityAnim; // Created somewhere else.
private void button1_Click(object sender, RoutedEventArgs e)
{
myLabel.Content = "Starting animation: " + _count++;
// Cancel any already-running animations and return
// the label opacity to 1.0.
_opacityAnim.Completed -= AnimationCompleted;
myLabel.BeginAnimation(Label.OpacityProperty, null);
myLabel.Opacity = 1.0;
// Create a new animation to fade out the label.
opacityAnim.Completed += AnimationCompleted;
// Start the countdown/animation.
myLabel.BeginAnimation(Label.OpacityProperty, opacityAnim);
}
private void AnimationCompleted(object sender, EventArgs e)
{
myTextbox.Text += Environment.NewLine + "Completed fired.";
}
I encountered the same problem.
In my project, I use a ProgressBar to give the user a period of time during which she/he could cancel the command. The user could cancel the command before the ProgressBar reached its end and called the actual work code in the handler of the Completed event.
The animation of the ProgressBar can be removed by calling myProgressBar.BeginAnimation(ProgressBar.ValueProperty, null), as you mentioned.
To prevent the handler of the Completed event of the removed animation from being executed, I check the animation first. Every time I create an DoubleAnimation for the ProgressBar, I assign it to a property. And in the handler of the Completed event, I will check first if this animation is the same as the one stored in the Property. If not, then just return. And every time I cancelled the animation, set the Property to null.
illustration:
DoubleAnimation CurrentAnimation {get; set; }
private void DoSomeWork(){
DoubleAnimation animation = new(0D, 100D, new(TimeSpan.FromSeconds(3)));
animation.Completed += (s,e) => {
lock(lockObj) {
if(animation != CurrentAnimation)
return;
// do some work here
}
CurrentAnimation = animation;
myProgressBar.BeginAnimation(ProgressBar.ValueProperty, animation);
}
private void CancelIt(){
lock(lockObj){
myProgressBar.BeginAnimation(ProgressBar.ValueProperty, null);
CurrentProperty = null;
}
}
I think there must be some nicer way to do it, but this will now make it run as designed.
精彩评论