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

Комментарии (10)


  1. virtyaluk
    29.08.2017 22:58

    Всегда удивляло насколько гибкая и продвинутая анимация в WPF. И насколько прожорлива к ресурсам. В свое время хотел запилить ripple-эффект а ля Material Design, но анимация нещадно жрала CPU и RAM. На этом все затухло. Умеют в Microsoft сделать крутую фичу, но не умеют довести ее до ума :(


    1. Kiel Автор
      30.08.2017 09:53

      Так хочется чуть чуть верить в то, что Microsoft доведет UWP до ума ))


      1. IL_Agent
        30.08.2017 10:54

        Эх, лучше wpf доводили… А так, боюсь, что uwp постигнет забвение, как и все остальные разработки, с выходом ккой-нибудь очередной версии винды (


        1. Kiel Автор
          30.08.2017 11:05

          И я совсем скачусь в циника ) Всё жду когда у них возникнет жгучее желание объединить все свои наработки и сделать единую WPF для linux / windows / mobile.

          Мечты.


          1. Dolbe
            30.08.2017 12:09

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


            1. Kiel Автор
              30.08.2017 14:08

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

              Смотрим кто победит ;) Но пока всё намекает на Html и JS приложения )


          1. expeon
            30.08.2017 12:34

            dotnet.github.io
            github.com/Microsoft/xaml-standard
            Все в наших с Вами руках :)


            1. Kiel Автор
              30.08.2017 14:10

              Тут только мои рук не хватит ))) Хоть стартап открывай!


  1. OlegKozlov
    30.08.2017 09:43
    +1

    анимации очень медленные

    Иногда помогает фреймрейт снизить: wpftutorial.net/FrameRate.html


    1. Kiel Автор
      30.08.2017 09:49

      Спасибо! Буду знать как с этим бороться )