LoginSignup
12
15

More than 3 years have passed since last update.

[WPF/xaml]Storyboardでアニメーションをつくる

Last updated at Posted at 2019-06-05

もくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f

Storyboard関連
- [WPF/xaml] xaml+C#で当番決めのためのルーレットを作る
- [WPF/xaml]Storyboardでアニメーションをつくる
- [WPF/xaml]Storyboardでアニメーションをつくる2(TargetPropertyの階層的な指定)

やりたいこと

こんな感じのスイッチを作って、押したら中の四角の部分が「ぐいーん」と左右に動いて、スイッチの入り切りを表現するようなものを作りたい。
image.png ⇔ image.png

サンプルコード

スイッチはCheckBoxを使い、そのStyleをうまいことやってやることで実現する。

MainWindow.xaml
<Window x:Class="WpfApp17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp17"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Background="Gray">
    <Grid>
        <!-- チェックボックス本体 -->
        <CheckBox>
            <CheckBox.Style>
                <Style TargetType="CheckBox">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type CheckBox}">

                                <!-- 見た目の設定 -->
                                <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                                    <!-- 外側の枠 -->
                                    <Border Name="BackgroundBorder" CornerRadius="2" Height="28" Width="60" BorderBrush="White" BorderThickness="2"/>
                                    <!-- 中のスイッチ部分 -->
                                    <Rectangle x:Name="slider" Stroke="#FFFF0000" HorizontalAlignment="Left" Width="12" Height="18" StrokeThickness="0" RenderTransformOrigin="0.5,0.5" Margin="0" Fill="White">
                                        <Rectangle.RenderTransform>
                                            <TransformGroup>
                                                <TranslateTransform X="5" Y="0" />
                                            </TransformGroup>
                                        </Rectangle.RenderTransform>
                                    </Rectangle>
                                </Grid>

                                <!-- ストーリーボードの設定 -->
                                <ControlTemplate.Resources>
                                    <Storyboard x:Key="OnChecking">
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="43" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                    <Storyboard x:Key="OnUmChecking">
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="5" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </ControlTemplate.Resources>

                                <!-- トリガーの設定 -->
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsChecked" Value="True">
                                        <!-- IsCheckedがTrueになるときのStoryboard開始指示 -->
                                        <Trigger.EnterActions>
                                            <BeginStoryboard Storyboard="{StaticResource OnChecking}" x:Name="OnChecking_BeginStoryboard" />
                                        </Trigger.EnterActions>
                                        <!-- IsCheckedがTrueから抜けるときのStoryboard開始指示 -->
                                        <Trigger.ExitActions>
                                            <BeginStoryboard Storyboard="{StaticResource OnUmChecking}" x:Name="OnUnchecking_BeginStoryboard" />
                                        </Trigger.ExitActions>

                                        <!-- その他プロパティの変化定義(チェック時) -->
                                        <Setter TargetName="slider" Property="Fill" Value="White" />
                                        <Setter TargetName="BackgroundBorder" Property="Background" Value="Orange" />
                                    </Trigger>

                                    <Trigger Property="IsChecked" Value="False">
                                        <!-- その他プロパティの変化定義(非チェック時) -->
                                        <Setter TargetName="slider" Property="Fill" Value="White" />
                                        <Setter TargetName="BackgroundBorder" Property="Background" Value="Gray" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </CheckBox.Style>
        </CheckBox>
    </Grid>
</Window>

説明

大きく分けて、4つの部分がある。

部分 概要
★チェックボックス本体 ベースになるCheckBoxを設置する
★見た目の設定 スイッチの見た目を、BorderやRectangleで作る
★ストーリーボードの設定 Storyboardを使って、どのコントロールのどのプロパティをどのように変化させる、ということを書く
★トリガーの設定 何をトリガーにして、ストーリーボードを動作させるかを書く

チェックボックス本体

ControlTemplateを作って、それをチェックボックスのStyleの中のTemplateにセットする形でチェックボックスをカスタムする。

a.xaml
<CheckBox>
    <CheckBox.Style>
        <Style TargetType="CheckBox">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CheckBox}">
                           ・
                           ・

見た目の設定

スイッチの見た目を、BorderやRectangleで作る。

a.xaml
<!-- ★見た目の設定 -->
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
    <!-- 外側の枠 -->
    <Border Name="BackgroundBorder" CornerRadius="2" Height="28" Width="60" BorderBrush="White" BorderThickness="2"/>
    <!-- 中のスイッチ部分 -->
    <Rectangle x:Name="slider" Stroke="#FFFF0000" HorizontalAlignment="Left" Width="12" Height="18" StrokeThickness="0" Fill="White">
        <Rectangle.RenderTransform>
            <TransformGroup>
                <TranslateTransform X="5" Y="0" />
            </TransformGroup>
        </Rectangle.RenderTransform>
    </Rectangle>
</Grid>

ここで設置しているコントロールのプロパティを動かすことで、スイッチが左右に動くことを表現したいので、動かすものに名前を付けておく。(ここでは「BackgroundBorder」「slider」の2つの名前を付けている)

それぞれのコントロールのWidthやHeight(幅/高さ)、あとTranslateTransformのXやY(スイッチの上下の位置)は、適当な値(デザイナーを見ながら、ちょうどよさそうな値を手探りで見つけた感じ。)。

StoryBoardの方で、このsliderのTranslateTransformのXを変化させるということをする。

ストーリーボードの設定

Storyboardの中に、Animationをセットして、どういう動きをさせるか定義する。
(19/06/06追記)下に、Storyboardについて追記しました。

a.xaml
<!-- ★ストーリーボードの設定 -->
<ControlTemplate.Resources>
    <Storyboard x:Key="OnChecking">
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="43" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="OnUmChecking">
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="5" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</ControlTemplate.Resources>

Storyboardには名前を付けておく。後でTriggerのところで、この名前を使って呼び出す。

DoubleAnimationUsingKeyFramesについて

DoubleAnimationUsingKeyFramesのプロパティ 概要
BeginTime アニメーションが開始するタイミング。これを00:00:05とすると、Checkboxを押してから5秒後にアニメ開始する。
Storyboard.TargetName アニメーションさせたいコントロールの名前。
Storyboard.TargetProperty アニメーションさせたいコントロールの、何のプロパティを変化させるか。

SplineDoubleKeyFrameについて

SplineDoubleKeyFrameのプロパティ 概要
Value 変化させたプロパティの値。
KeyTime 何秒間で、プロパティの値をValueで設定した値にもっていくか。00:00:00.2とすると、0.2秒でプロパティがValueの値まで変化する。

トリガーの設定

何をトリガーにして、ストーリーボードを動作させるかを書く。

a.xaml
<ControlTemplate.Triggers>
    <Trigger Property="IsChecked" Value="True">
        <!-- IsCheckedがTrueになるときのStoryboard開始指示 -->
        <Trigger.EnterActions>
            <BeginStoryboard Storyboard="{StaticResource OnChecking}" x:Name="OnChecking_BeginStoryboard" />
        </Trigger.EnterActions>
        <!-- IsCheckedがTrueから抜けるときのStoryboard開始指示 -->
        <Trigger.ExitActions>
            <BeginStoryboard Storyboard="{StaticResource OnUmChecking}" x:Name="OnUnchecking_BeginStoryboard" />
        </Trigger.ExitActions>

        <!-- その他プロパティの変化定義(チェック時) -->
        <Setter TargetName="slider" Property="Fill" Value="White" />
        <Setter TargetName="BackgroundBorder" Property="Background" Value="Orange" />
    </Trigger>
                  ・
                  ・
</ControlTemplate.Triggers>
Triggerにセットするもの 概要
Trigger.EnterActions Triggerで監視しているプロパティがValueの値になるときに行うStoryboardを指定する
Trigger.ExitActions Triggerで監視しているプロパティがValueの値から抜けるときに行うStoryboardを指定する
BeginStoryboard Storyboardプロパティに、上で作ったStoryboardの名前を指定する

備考

今回、DoubleAnimationUsingKeyFramesを使って、TranslateTransformXというdoubleの値を変化させることでアニメーションするということをしたが、ほかにも色を変化させるとか、いろいろできるっぽい。要調査。

補足 Storyboardの使い方(19/06/06追記)

Storyboardの使い方の基本は、下記のような使い方をする。

a.xaml
<!-- doubleの値を変化させるStoryboard -->
<Storyboard x:Key="OnChecking" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="RenderTransform.(TransformGroup.Children)[0].(TranslateTransform.X)">
        <DiscreteDoubleKeyFrame KeyTime="00:00:02" Value="43" /> <!-- 値の変化がリニアではなく、指定した時間に、急に指定した値になる -->
        <LinearDoubleKeyFrame KeyTime="00:00:04" Value="5" />    <!-- 値が現在値から目的値にリニアに変化していく -->
        <EasingDoubleKeyFrame KeyTime="00:00:06" Value="43" />   <!-- イージングができる(イージング関数を指定しなければLinearと同じ) -->
        <SplineDoubleKeyFrame KeyTime="00:00:08" Value="5" />    <!-- 変化の加速や減速ができる -->
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

<!-- 色を変化させるStoryboard -->
<Storyboard x:Key="OnCheckingForColor" RepeatBehavior="Forever">
    <ColorAnimationUsingKeyFrames  BeginTime="00:00:00"  Storyboard.TargetName="backColor" Storyboard.TargetProperty="Color">
        <DiscreteColorKeyFrame KeyTime="00:00:02" Value="Red"/>
        <LinearColorKeyFrame KeyTime="00:00:04" Value="Green"/>
        <EasingColorKeyFrame KeyTime="00:00:06" Value="Blue"/>
        <SplineColorKeyFrame KeyTime="00:00:08" Value="Yellow"/>
    </ColorAnimationUsingKeyFrames>
</Storyboard>

■Storyboardクラス
ここに、トリガーからの呼び出しのための名前や、繰り返しの設定などを書く。

■xxxxxAnimationUsingKeyFramesクラス
AnimationTimelineクラスを継承したものをここで使う。
AnimationTimelineを継承するクラスはたくさんあり、ここで「どんなプロパティを変化させるか」が決まる。
例えばDoubleAnimationUsingKeyFramesを使うなら、double型のプロパティを変化させる、となり、ColorAnimationUsingKeyFramesを使うなら色を変化させる、となる。

■xxxxKeyFrameクラス
プロパティの種類(型)と、そのプロパティをどのように変化させるか、でここに使うクラスが決まる。
例えば、double型のプロパティを、初期値から徐々に目的の値まで変化させたい、となると、LinearDoubleKeyFrameを使う。
Color型のプロパティを、指定した時間に、徐々にではなくいきなりその色に変えたい、となると、DiscreteColorKeyFrameを使う。

xxxxKeyFrameクラスは、プロパティの種類ごとに、4つずつ派生クラスがあるっぽい。
例えば、double型用のクラスであれば下記の4つ。

  • DiscreteDoubleKeyFrame
  • LinearDoubleKeyFrame
  • EasingDoubleKeyFrame
  • SplineDoubleKeyFrame

見た限り、Color用についても同じものがあった。おそらく他の種類についても同じだと思われる。

参考

AnimationTimelineクラス
「DoubleAnimationUsingKeyFrames」などの元になっているクラス。
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.media.animation.animationtimeline?view=netframework-4.8

Storyboardで変化させられるプロパティには、このページによるとこれだけある。
image.png

12
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
15