Как лучше всего запустить анимацию при изменении связанного значения? - PullRequest
7 голосов
/ 14 июля 2009

Это часто встречающаяся ситуация:

В представлении у вас есть элемент управления, связанный со свойством ViewModel (поддерживаемый INotifyPropertyChanged). Например:

<TextBlock Text="{Binding Path=Subtotal}"/>

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

На ум приходят следующие варианты:

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

Какой из них вам нравится / используется? Я пропустил какие-либо варианты?

P.S. Было бы неплохо (но не критично), если бы решение предоставило возможность сначала запустить анимацию и отразить изменение значения после ее завершения.

Ответы [ 2 ]

3 голосов
/ 14 июля 2009

Хорошо, это то, к чему я пришел после некоторых экспериментов.

Я создал триггер Expression Blend 3 со свойством зависимости (я назвал его Subscription). Я привязываю подписку к тому же значению, к которому привязан мой TextBlock, и этот триггер присоединен к действию ControlStoryboardAction из Expression Blend 3.

Вот триггер:

public class DataTriggerPlus : TriggerBase<DependencyObject>
{
    public static readonly DependencyProperty SubscriptionProperty =
        DependencyProperty.Register("Subscription", 
            typeof(string),
            typeof(DataTriggerPlus),
            new FrameworkPropertyMetadata("",
              new PropertyChangedCallback(OnSubscriptionChanged)));

    public string Subscription
    {
        get { return (string)GetValue(SubscriptionProperty); }
        set { SetValue(SubscriptionProperty, value); }
    }

    private static void OnSubscriptionChanged(DependencyObject d,
      DependencyPropertyChangedEventArgs e)
    {
        ((DataTriggerPlus)d).InvokeActions(null);
    }
}

Вот как оно прикреплено к раскадровке:

<TextBlock x:Name="textBlock" Text="{Binding TestProp}" Background="White">
    <i:Interaction.Triggers>
        <local:DataTriggerPlus Subscription="{Binding TestProp}">
            <im:ControlStoryboardAction 
                Storyboard="{StaticResource Storyboard1}"/>
        </local:DataTriggerPlus>
    </i:Interaction.Triggers>
</TextBlock>

Мне очень нравится этот подход, отличная работа дизайнеров Blend 3!

Редактировать: отвечая на комментарий Дрю ...

Да, он поставляется с Blend. Вы можете просто включить Microsoft.Expression.Interactions.dll и System.Windows.Interactivity в ваш проект.

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

2 голосов
/ 14 июля 2009

Вы можете создать триггер, который запустит анимацию.

Примерно так:

<Style>
    <Style.Triggers>
       <Trigger 
            Property="ViewModelProperty"
            Value="True">
            <Trigger.EnterActions>
                 <BeginStoryboard Storyboard="YourStoryBoard" />
            </Trigger.EnterActions>
       </Trigger>
    </Style.Triggers>
</Style>

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

Я пытался использовать EventTriggers для привязки к завершенным событиям, но это также создает некоторые сложности. Подробнее см. здесь .

...