Как лучше всего запустить анимацию при изменении связанного значения?
-
13-09-2019 - |
Вопрос
Часто возникает такая ситуация:
В представлении у вас есть элемент управления, привязанный к свойству ViewModel (поддерживаемый INotifyPropertyChanged).Например:
<TextBlock Text="{Binding Path=Subtotal}"/>
Когда свойство меняется, вам необходимо привлечь внимание пользователя к этому факту с помощью креативной анимации.Как я могу использовать тот факт, что представление уже подключено к уведомлению, и избежать создания большей части дополнительного кода (или, по крайней мере, создать его один раз и использовать повторно).Триггеры данных, вероятно, являются лучшим выбором, но я не знаю, как заставить их срабатывать при любом изменении значения, а не при каком-то конкретном значении.
На ум приходят следующие варианты:
- вызовите дополнительное событие во ViewModel, подпишитесь на код программной части View.
- создайте триггер данных, привязанный к упомянутому свойству, с помощью преобразователя, который будет возвращать true, если значение меняется.
- создайте триггер данных, привязанный к новому логическому свойству ViewModel, которое используется для «сигнализации» об изменении.
- создайте поведение, прикрепленное к элементу управления, которое будет подписываться на изменение свойства зависимостей элемента управления и запускать анимацию.
Какой из них вам нравится/используется?Я пропустил какие-то варианты?
P.S.Было бы хорошо (но не критично), если бы решение обеспечивало возможность сначала запускать анимацию и отражать изменение значения после ее завершения.
Решение
Хорошо, вот к чему я пришел после некоторых экспериментов.
Я создал триггер Expression Blend 3 со свойством зависимости (я назвал его «Подписка»).Я привязываю подписку к тому же значению, к которому привязан мой 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 в свой проект.
И да, это многословно (я спросил, нашел ли кто-нибудь хороший способ применить поведение через стили в этом вопросе) - но есть и преимущество гибкости.Например, вы можете не только запустить раскадровку, но и переключить состояние или выполнить какое-либо другое действие с помощью того же триггера.
Другие советы
Вы можете создать триггер, который запустит анимацию.
Что-то вроде этого:
<Style>
<Style.Triggers>
<Trigger
Property="ViewModelProperty"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="YourStoryBoard" />
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Что касается проблемы с установкой значения после завершения анимации, это немного неприятно.Насколько я знаю, вам нужно будет использовать завершенное событие в раскадровке, для этого требуется код, чего вы хотите избежать с помощью MVVM.
Я пробовал использовать EventTriggers для привязки к завершенным событиям, но это также создает некоторые сложности.Видеть здесь Больше подробностей.