Анимация в WPF и Blend SDK

Всем добрый день! В этой статье я опишу простой способ запуска анимации с помощью инструмента 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>

Демонстрация результата:

image

Теперь надо сделать их анимириванными, для этого перенесем наши вычисления позиции из 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>

И анимации запустились:

image

Jobs done!

Мы не запускаем их лишний раз и они работают так как нам нужно. К тому же Blend SDK содержит множество других простых и, при этом, невероятно полезных вещей — советую ознакомиться:

Microsoft.Expression.Interactions documentation
System.Windows.Interactivity documentation

И, возвращаясь к началу статьи, хочу обратить внимание на второй пункт — то, что анимации очень медленные. Да — они такие, что с этим делать я не знаю, но тем не менее буду стараться выяснить.

Всем спасибо за внимание!

Ссылка на проект на github
Поделиться публикацией

Похожие публикации

Комментарии 11
    0
    Всегда удивляло насколько гибкая и продвинутая анимация в WPF. И насколько прожорлива к ресурсам. В свое время хотел запилить ripple-эффект а ля Material Design, но анимация нещадно жрала CPU и RAM. На этом все затухло. Умеют в Microsoft сделать крутую фичу, но не умеют довести ее до ума :(
      0
      Так хочется чуть чуть верить в то, что Microsoft доведет UWP до ума ))
        0
        Эх, лучше wpf доводили… А так, боюсь, что uwp постигнет забвение, как и все остальные разработки, с выходом ккой-нибудь очередной версии винды (
          0
          И я совсем скачусь в циника ) Всё жду когда у них возникнет жгучее желание объединить все свои наработки и сделать единую WPF для linux / windows / mobile.

          Мечты.
            0

            Xamarin же. Но он не умеет в Linux, вроде.

              0
              Сейчас ничего не умеет под Linux. Пытались реализовать Moonlight и забросили в итоге. Это еще будущее. И тем не менее — даже в Xamarin / UWP / WPF присутствует фрагментированность.

              Смотрим кто победит ;) Но пока всё намекает на Html и JS приложения )
              0
              dotnet.github.io
              github.com/Microsoft/xaml-standard
              Все в наших с Вами руках :)
                0
                Тут только мои рук не хватит ))) Хоть стартап открывай!
                0
                У самого Microsoft, конечно же, такого желания не возникнет, т. к. не понятно как на этом можно заработать денег. Так что остается надеяться только на open source. Например, есть многобещающий проект github.com/AvaloniaUI/Avalonia
          +1
          анимации очень медленные

          Иногда помогает фреймрейт снизить: wpftutorial.net/FrameRate.html
            0
            Спасибо! Буду знать как с этим бороться )

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое