Why, after adjusting the image height, this property immediately stops working

I have a problem that is killing me. Below is a simple example.

<Grid Name="_grid"> <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="36,33,0,0" Name="button" VerticalAlignment="Top" Width="75" /> <Button Content="Enlarge through animation" Height="23" Margin="172,33,24,0" Name="_animateButton" VerticalAlignment="Top" Click="_animateButton_Click" /> <Button Content="Enlarge through simple heigh changing" Height="23" Margin="172,62,24,0" Name="_resizeButton" VerticalAlignment="Top" Click="_resizeButton_Click" /> </Grid> 

And in code

 private void _resizeButton_Click(object sender, RoutedEventArgs e) { button.Height += 10; } private void _animateButton_Click(object sender, RoutedEventArgs e) { Storyboard storyboard = new Storyboard(); DoubleAnimation animation = new DoubleAnimation(button.Height + 10, new Duration(new TimeSpan(0, 0, 0, 1))); storyboard.Children.Add(animation); Storyboard.SetTargetName(animation, button.Name); Storyboard.SetTargetProperty(animation, new PropertyPath(HeightProperty)); storyboard.Begin(_grid); } 

The application is as follows

alt text

After immediately pressing the left button _resizeButton. Then I press _animateButton - the left button is slowly increasing. After that, I click _resizeButton again and nothing happens. Why is this?

I noticed that the same thing happens when animating the Top property

+6
c # animation wpf dependency-properties
source share
3 answers

To understand the behavior, you need to know how animation works in the WPF property system: animation does not work, setting the property for different values ​​over time. They work by providing an effective value for a property that temporarily takes precedence over a "base" value. This is a subtle difference, but it is what makes you peel off here.

When you first come across an animation system, most people think it works by repeatedly calling the "set" accessor for a property. But this is not so - if you set the property this way before starting the animation, the original value that you set remains in place. It's just that getter will return the value provided by the animation system, instead of returning a "local" value. In fact, you can even change the β€œlocal” value by setting the property during the animation, but this local value will not become visible until the animation stops.

Actually, the ownership system does a lot of this - it's not just animation. This reference topic contains 11 different places from which a property value can come. Animation is the second highest priority. Properties set in the usual way through the set accessory (or through attributes in Xaml) are the next highest priority, but you can see that templates, styles, and triggers provide other sources if there is no local property value.

The WPF animation system has the concept of a β€œbase” value and essentially the value of the next highest priority available after the current animated value. If you have a local value, this will be the base value, but if the base value is not obtained from one of the other sources listed in this article.

The result of all that there is no easy way to do what you are trying to do. I think you want the animation to run until completion, and that the property retains the final animation value until you set a local value for something else.

If you tell the animation to stop after it is completed, the effective value will revert to the base value. (As you say in the commentary, it shrinks after the animation finishes.) And if you let the animation know that it will be completed (this is the default behavior), then the animation will forever provide a value that has higher priority than local values, so you see that manually resizing the button no longer works. Thus, none of the options does what you want.

There are two ways to handle this. @ responderNS5 posted one - handle the completion of the animation, change the local value to reflect the final value of the animation, and then stop the animation. (You might think that this is a kind of demotion β€” it will convert it from a high-priority but transitory value of the property provided by the animation to a slightly lower priority but more constant local value.) I would be inclined to change the code a bit:

 private void myDoubleAnimation_Completed(object sender, EventArgs e) { // Animation complete, but holding, so Height will currently return // return the final animated value. double finalHeight = button.Height; // Remove the animation. button.BeginAnimation(Button.HeightProperty, null); // Modify the local value to be the same as the final animated value. button.Height = finalHeight; } 

What I changed here is that the code no longer tries to guess where the animation system ended up - it just reads any value that the animation system sets for this property and makes it a new local value. I prefer += 10 in the Completed handler, which for me looks like a slightly fragile duplication of logic.

Another way to handle this is to remove the animation where you are trying to update the property:

 private void _resizeButton_Click(object sender, RoutedEventArgs e) { double currentHeight = button.Height; button.BeginAnimation(Button.HeightProperty, null); button.Height = currentHeight + 10; } private void _animateButton_Click(object sender, RoutedEventArgs e) { DoubleAnimation animation = new DoubleAnimation { By = 10, Duration = new Duration(TimeSpan.FromSeconds(1)) }; button.BeginAnimation(Button.HeightProperty, animation); } 

This seems a little more reliable for me, because it will work even if the animation was not completed by the time the resize button was clicked.

+15
source share

You need to set FillBehavior DoubleAnimation to Stop instead of Hold . See this article:

+3
source share

Rick's answer will not work as you indicated. In your case, you need to set the animation of the animated property (button height) to null so that you can make additional changes to this property.

http://msdn.microsoft.com/en-us/library/aa970493.aspx

  private void _animateButton_Click(object sender, RoutedEventArgs e) { DoubleAnimation animation = new DoubleAnimation(); animation.By = 10; animation.Duration = new Duration(TimeSpan.FromSeconds(1)); animation.Completed += new EventHandler(myDoubleAnimation_Completed); button.BeginAnimation(Button.HeightProperty, animation); } private void myDoubleAnimation_Completed(object sender, EventArgs e) { button.BeginAnimation(Button.HeightProperty, null); button.Height += 10; } 

Change the _animateButton_Click handler to the above. In addition, add a second event handler so that after the animation is complete, we can remove the link to the "animation". A second statement in myDoubleAnimation_Completed exists because when the animation link is deleted, an unanimated value is returned.

+3
source share

All Articles