Всем добрый день!
В этой статье я опишу простой способ запуска анимации с помощью инструмента Blend SDK от Microsoft.
С анимациями в WPF дела обстоят не очень легко и их стараются избежать по нескольким причинам. Первая — их тяжело запускать и сложно останавливать. Вторая — они не очень быстрые.
Разберемся с запуском — что же такого «сложного». Нарисуем простой ItemsControl, внутри которого есть Canvas и размещаются маленькие шарики.
<ItemsControl ItemsSource="{Binding Bubbles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse
Width="10"
Height="10"
Fill="#FF616AB6" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Демонстрация результата:
Теперь надо сделать их анимириванными, для этого перенесем наши вычисления позиции из Canvas.Left и Canvas.Top в анимацию, которая будет задавать Canvas.Left и Canvas.Top элемента. Разместим её в ресурсах каждого Ellipse'а.
<Ellipse.Resources>
<Storyboard x:Key="yanimation">
<DoubleAnimation
Storyboard.Target="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}}"
Storyboard.TargetProperty="(Canvas.Top)"
Duration="0:0:1" To="{Binding Y}" />
</Storyboard>
<Storyboard x:Key="xanimation">
<DoubleAnimation
Storyboard.Target="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}}"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:1" To="{Binding X}" />
</Storyboard>
</Ellipse.Resources>
Но теперь вопрос — когда запускать анимации? Однозначно нам нужно запускать при появлении элемента, то есть добавим:
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard Storyboard="{StaticResource xanimation}" />
</EventTrigger>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard Storyboard="{StaticResource yanimation}" />
</EventTrigger>
</Ellipse.Triggers>
Но что теперь? По хорошему можно забиндить запуск анимации на самих себя же, добавив в Binding каждого вычисления To по NotifyOnTargetUpdated=True. Тогда можно написать
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard Storyboard="{StaticResource xanimation}" />
</EventTrigger>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard Storyboard="{StaticResource yanimation}" />
</EventTrigger>
</Ellipse.Triggers>
И всё работает. Но не всё так просто. В живом проекте такой подход работать отказался — скорее всего из-за зацикливания вызовов. Также где угодно можно написать NotifyOnTargetUpdated=True и анимации будут вызываться лишний раз. Нам же нужно, чтобы анимации запускались только по изменении свойств X и Y. Тут то и приходит нам на выручку Blend SDK с его библиотеками Microsoft.Expression.Interactions и System.Windows.Interactivity.
Добавляем их в проект (я добавлял через nuget). И прописываем нужные нам xmlns:
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Теперь можно внутри каждого Ellipse'а написать:
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding X}">
<ei:ControlStoryboardAction Storyboard="{StaticResource xanimation}" />
</ei:PropertyChangedTrigger>
<ei:PropertyChangedTrigger Binding="{Binding Y}">
<ei:ControlStoryboardAction Storyboard="{StaticResource yanimation}" />
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
И анимации запустились:
Jobs done!
Мы не запускаем их лишний раз и они работают так как нам нужно. К тому же Blend SDK содержит множество других простых и, при этом, невероятно полезных вещей — советую ознакомиться:
Microsoft.Expression.Interactions documentation
System.Windows.Interactivity documentation
И, возвращаясь к началу статьи, хочу обратить внимание на второй пункт — то, что анимации очень медленные. Да — они такие, что с этим делать я не знаю, но тем не менее буду стараться выяснить.
Всем спасибо за внимание!
Автор: Kiel