2011-01-05 3 views
6

У меня проблема, которая меня убивает. Ниже приведен простой примерПочему после анимации установки высоты это свойство сразу перестает работать

<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> 

И в код

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

Применение выглядит следующим образом

alt text

После нажатия левой кнопки _resizeButton увеличить сразу. Затем я нажимаю _animateButton - левая кнопка медленно увеличивается. После этого я снова нажимаю _resizeButton и ничего не происходит. Почему это?

Я заметил, что то же самое, когда анимировать Top недвижимость

ответ

15

Чтобы понять поведение, вам необходимо знать о том, как анимация работает в системе свойств WPF: анимация не работает, устанавливая свойство для разных значений с течением времени. Они работают, предоставляя эффективное значение для свойства, которое временно имеет приоритет над значением «base». Это тонкое различие, но это то, что заставляет вас отклеиваться здесь.

Когда вы впервые сталкиваетесь с системой анимации, большинство людей думает, что она работает, неоднократно называя «set» accessor для свойства. Но это не так - если вы установили свойство таким образом, прежде чем начать анимацию, исходное значение, которое вы установили, остается на месте. Просто getter вернет значение, предоставленное системой анимации, вместо того, чтобы возвращать «локальное» значение. Фактически, вы даже можете изменить «локальное» значение, установив свойство во время работы анимации, но это локальное значение не станет видимым, пока анимация не остановится.

На самом деле, система собственности делает много таких вещей - это не просто анимация. В этом help topic перечислены 11 разных мест, из которых может исходить значение свойства. Анимация - второй по высоте приоритет. Свойства, установленные обычным способом через аксессуар «set» (или через атрибуты в Xaml), являются следующим самым высоким приоритетом, но вы можете видеть, что шаблоны, стили и триггеры предоставляют другие источники, если локальное значение свойства отсутствует.

Система анимации в WPF имеет концепцию «базового» значения, и это, по сути, значение следующего наивысшего приоритета, доступное после текущего анимированного значения. Если у вас есть локальное значение, это будет базовое значение, но если базовое значение не будет получено из одного из других источников, перечисленных в этой статье.

Результат всего того, что нет простого способа сделать то, что вы пытаетесь сделать. Я думаю, что вы хотите, чтобы анимация выполнялась до завершения, и чтобы свойство сохраняло свою конечную анимационную ценность до тех пор, пока вы не установили локальное значение на что-то еще.

Если вы сообщите анимации о прекращении после ее завершения, эффективное значение вернется к базовому значению. (Как вы говорите в комментарии, он сжимается после завершения анимации.) И если вы сообщите анимации о том, что она будет завершена (это поведение по умолчанию), тогда анимация навсегда будет предоставлять значение, имеющее более высокий приоритет, чем локальные значения , поэтому вы видите, что изменение размера кнопки вручную больше не работает. Поэтому ни один из вариантов не делает то, что вы хотите.

Есть два способа справиться с этим. @ responderNS5 разместил один - обработает завершение анимации, изменит локальное значение, чтобы отразить окончательное значение анимации, а затем остановит анимацию. (Вы могли подумать, что это своего рода понижение в должности - оно преобразует его из высокоприоритетного, но преходящего значения свойства, предоставленного анимацией, в немного более низкое приоритетное, но более постоянное локальное значение.) Я был бы склонен к изменению код немного:

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; 
} 

что я изменил здесь является то, что код больше не пытается угадать, где система анимации закончилась - это просто читает любое значение, система анимации установила свойство и делает, что новое местное значение. Я предпочитаю это для += 10 в обработчике Completed, который для меня похож на слегка хрупкое дублирование логики.

Другой способ борьбы с ним, чтобы удалить анимацию в точке, в которой вы пытаетесь обновить свойство:

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

Это кажется немного более надежным для меня, потому что это будет работать, даже если анимация не была завершена к моменту нажатия кнопки изменения размера.

3

Вы должны установить FillBehavior из DoubleAnimation в Stop вместо Hold. Смотрите эту статью:

+0

Спасибо за ваш ответ, но после настройки анимации. FillBehavior = FillBehavior.Установите его усадку до последнего значения высоты. Я делаю что-то неправильно? – bizon

+0

Спасибо !!! Сэкономил мне часы! – Rocklan

3

ответ Рик не будет работать, как вы указали на это. В вашем случае вам нужно установить анимацию анимированного свойства (высота кнопки) равным нулю, чтобы вы могли вносить дополнительные изменения в это свойство.

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; 
    } 

Измените свой _animateButton_Click обработчик к одному указанному выше. Кроме того, добавьте второй обработчик событий, чтобы после завершения анимации мы могли удалить ссылку на «анимацию». Второй оператор в myDoubleAnimation_Completed существует, потому что, когда ссылка на анимацию удалена, возвращается неанимированное значение.

+0

Идеальный .. Есть и другие сообщения, на которые нет ответа. Я поставлю ссылку на ваш ответ – Sayka

Смежные вопросы