Почему после анимации установки высоты это свойство сразу перестает работать - PullRequest
6 голосов
/ 05 января 2011

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

<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

Ответы [ 3 ]

16 голосов
/ 05 января 2011

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

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

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

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

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

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

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

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 голосов
/ 05 января 2011

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

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 находится там, потому что, когда ссылка на анимацию удаляется, возвращается неанимированное значение.

3 голосов
/ 05 января 2011

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

...