#wpf #xaml
#wpf #xaml
Вопрос:
Как мне плавно анимировать предыдущее значение связанного свойства в его новое значение?
Допустим, у нас есть следующий холст и строка.
<Canvas>
<Line
Canvas.Top="0"
Stroke="#887FFF00"
StrokeThickness="2"
X1="0" Y1="0"
X2="0" Y2="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Canvas}}"
Canvas.Left="{Binding Position}"
>
</Line>
</Canvas>
Горизонтальное положение строки определяется свойством Position, привязанным к Canvas.Оставлено присоединенное свойство. Когда позиция изменяется, скажем, со 100 на 200, я хотел бы анимировать положение строки из ее предыдущего значения плавно в ее новое значение.
Как мне это сделать?
Ответ №1:
Безусловно, самый простой способ плавно анимировать свойство в wpf — это использовать анимацию.
Однако для ваших требований в анимации есть несколько хитрых моментов.
Вы не можете привязать «from» или «to» к анимации, поэтому для этого потребуется создать анимацию в коде.
Для анимации значения вам нужно свойство dependency и, следовательно, объект dependency.
Вы могли бы рассмотреть возможность создания viewmodel, который предоставляет position, в объект зависимости. Затем вы можете создать новую анимацию в коде, когда вам нужно ее анимировать, и просто сохранить там свою привязку.
Или… вы могли бы определить раскадровку как ресурс и изменить это в коде для создания вашей анимации.
Возможно, вам не нравится делать viewmodel объектом зависимости.
Если вас устраивает немного «скрытого кода» в вашем представлении, то это объект зависимости. Вы могли бы в качестве альтернативы добавить свойство зависимости в свое окно. Привяжите это к Position, а затем анимируйте canvas.слева от вашей строки или другого свойства зависимости, к которому привязана строка.
Ответ №2:
Вместо прямой привязки к исходному свойству с использованием {Binding}
синтаксиса в XAML, вы могли бы самостоятельно подписаться на PropertyChanged
событие модели представления в представлении и анимировать свойство программно, например:
private void OnViewLoaded(object sender, RoutedEventArgs e)
{
ViewModel viewModel = DataContext as ViewModel;
if (viewModel != null)
{
Canvas.SetLeft(line, viewModel.Position);
viewModel.PropertyChanged = OnPropertyChanged;
}
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Position")
{
double from = Canvas.GetLeft(line);
if (double.IsNaN(from))
from = 0;
ViewModel viewModel = sender as ViewModel;
if (viewModel != null)
{
DoubleAnimation doubleAnimation = new DoubleAnimation()
{
From = from,
To = viewModel.Position,
Duration = TimeSpan.FromSeconds(1)
};
line.BeginAnimation(Canvas.LeftProperty, doubleAnimation);
}
}
}
XAML:
<Canvas Width="100" Height="100" Background="Beige">
<Line x:Name="line"
Canvas.Top="0"
Stroke="#887FFF00"
StrokeThickness="2"
X1="0" Y1="0"
X2="0" Y2="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Canvas}}">
</Line>
</Canvas>
Это пример ситуации, когда вы хотите реализовать пользовательскую логику, зависящую от представления, в представлении, и вы не хотите делать это в XAML. Это никоим образом не нарушает шаблон MVVM.