連載
» 2011年03月01日 00時00分 UPDATE

連載:WPF入門:第10回 WPFの「入力イベントとアニメーション」を学ぼう (2/2)

[岩永信之(http://ufcpp.net/),著]
前のページへ 1|2       

■アニメーション

 WPFのアニメーションは以下のような特徴を持っている。

  • 実時間ベース: フレーム・レート(=1秒間に描画されるコマ数のこと)が変わっても再生速度は変わらない。
  • マークアップ・ベース: XAMLコード中に記述でき、Expression Blendなどのツールでの読み書きが容易。

低レベルなアニメーション制御

 WPFでは、後述するストーリーボードという高機能なアニメーション制御機構を利用できるが、一応、低レベルなアニメーション制御の仕方についても補足説明しておこう。

 まず、最も原始的なアニメーション制御の仕方として、UI要素の描画タイミングを拾って、すべて自前でアニメーションを管理する方法が考えられる。描画タイミングは、CompositionTargetクラス(System.Windows.Media名前空間)のRendering静的イベントによって拾うことができるので、このイベントを利用することになる。

 次に、PresentationCoreアセンブリ(=WPF以外のフレームワークでも利用することを想定した汎用的な機能のみを集めた部分)レベルでは、TimeLineクラス(System.Windows.Media名前空間)や、UIElementクラスのBeginAnimationメソッドなどを利用することで、UI要素ごとのアニメーションを制御できる。ただし、この方法ではXAMLコード中に記述できず、分離コードが必要となる。

 次節以降で説明するストーリーボードは、PresentationFrameworkアセンブリ(=WPF固有の機能)レベルのアニメーション制御機構である。

ストーリーボード

 多くの場合、1つのUI要素だけをアニメーションさせることはほとんどなく、複数のUI要素が絡むことになる。そこで、「どのUI要素をどのタイミングで動かすか」という情報を複数まとめて管理するものをストーリーボード(storyboard: 絵コンテ)呼び、Storyboardクラス(System.Windows.Media.Animation名前空間)がその役割を担う。

 ストーリーボードは、XAMLコードとして記述できるので、原理的には「メモ帳で編集可能」なものである。しかし、複雑なストーリーボードは最終的なXAMLコードも複雑になり、編集ツールのサポートなしでは厳しいだろう。残念ながら、Visual Studioではストーリーボードの編集が容易ではなく、Expression Blendの利用がほぼ必須といえる。Movie 4に、Expression Blendを利用したストーリーボード編集の例を示す。


 この例では、だ円を左右に振動させるストーリーボードを作っている。また、後述するビヘイビアを使って、だ円の上にマウス・ポインタが乗った瞬間にストーリーボードの再生を開始するよう設定している。

ストーリーボードの内容

 Movie 4に示した編集の結果となるストーリーボードをList 1に示す。

<Storyboard x:Key="ShakeStoryboard">
  <DoubleAnimationUsingKeyFrames
     Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
     Storyboard.TargetName="ellipse">

    <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="10">
      <EasingDoubleKeyFrame.EasingFunction>
        <CircleEase EasingMode="EaseIn"/>
      </EasingDoubleKeyFrame.EasingFunction>
    </EasingDoubleKeyFrame>

    <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-20"/>
    <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="20"/>
    <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="-20"/>

    <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-0">
      <EasingDoubleKeyFrame.EasingFunction>
        <CircleEase EasingMode="EaseOut"/>
      </EasingDoubleKeyFrame.EasingFunction>
    </EasingDoubleKeyFrame>

  </DoubleAnimationUsingKeyFrames>
</Storyboard>

List 1: ストーリーボードの例

 この例ではストーリーボードのコンテンツであるタイムライン(=<DoubleAnimationUsingKeyFrames>要素など)は1つしかないが、Figure 1に示すように、ストーリーボードは複数のタイムラインを持つことができる。

ap-introwpf_10_01.gif Figure 1: ストーリーボードとタイムライン

 各タイムラインは、Figure 2に示すように、「どのUI要素のどのプロパティを、どう動かすか」という情報を持っている。動かし方ごとに異なるクラスとなっている。

ap-introwpf_10_02.gif Figure 2: タイムラインの構成と種類

 ちなみに、TargetProperty属性には、データ・バインディングで使うBindingマークアップ拡張のPathプロパティと同様の構文でパスを指定する(第5回参照)。

キー・フレームを使ったタイムライン

 Expression Blendでの編集結果として得られるために最も目にする機会が多くなる、キー・フレームを使ったタイムライン(=型名がUsingKeyFramesで終わっているタイムライン)について、もう少し詳しく説明しておこう。

 キー・フレームとは、アニメーションにおける変化の基点となるフレーム(=いわゆる「コマ」)のことをいう。アニメーションは、いわゆるパラパラ漫画の要領で、1フレームごとに位置や色などを指定することでも制御可能である。しかし、この方法には以下のような問題がある。

  • アニメーションが長くなると、位置や色などの情報が膨大になる
  • フレーム・レート(=1秒間に何回描画を行うか)の変化への対応が難しい(処理落ちなどでフレーム・レートが落ちた場合、アニメーションがスローモーション再生されるというようなことがある)

そこで、WPFでは、とびとびにキー・フレームというものを設定し、位置や色などの情報はキー・フレームに対してだけ行い、その間の値は補間によって生成することでアニメーションを制御している。補間は時刻に応じて行われるため、フレーム・レートが変化してもアニメーションの再生速度は変わらない。  それでは、具体的なキー・フレームの設定方法を見ていこう。WPFでは、、Figure 3に示すように、「どの時刻までにどういう値になっているべきか」と「値の補間方法」を指定する。

ap-introwpf_10_03.gif Figure 3: キー・フレームの構成と種類

 図中の4つの補間方法はそれぞれ以下のようなものである。

  • Discrete: 指定時刻になった瞬間に、離散的に値を変化させる。要するに、補間は行われず、値は指定時刻に一気に切り替わる。
  • Linear: 線形補間。値は一定速度で徐々に変化していく。
  • Spline: スプライン曲線を用いた補間。値を滑らかに変化させることができ、線形補間(=一定速度の直線的な動きしかできない)を利用する場合よりキー・フレームの間隔を長めに取っても自然な動きを実現しやすい。
  • Easing: 任意の関数を使って補間。補完に使う関数のことをイージング関数(easing function: easingは「急な変化を緩和する」という意味)と呼ぶ。WPF 4で追加された。

 イージングに使う関数は、もちろん自作することもできるし、いくつかのイージング関数は標準で提供されている。標準提供されているものには、例えば、ボールが弾むような軌跡を描く「BounceEase」関数や、バネの伸び縮みのような軌跡の「ElasticEase」関数(いずれもSystem.Windows.Media.Animation名前空間)などがある。以下のデモが参考になるだろう(Silverlightに関するデモだが、イージング関数の種類はWPFでも同様である)。

トリガー

 ストーリーボードを定義しただけではアニメーションは開始されず、別途、開始や停止のきっかけ(=「トリガー」と呼ぶ)が必要である。

 WPFでは、もともと、FrameworkElementクラスのTriggersプロパティを使ってトリガーを設定することができた。例えば、<Window>要素の直下に以下のようなXAMLコードを記述することで、ウィンドウのロード完了と同時にストーリーボードを開始できる。

<Window.Triggers>
  <EventTrigger RoutedEvent="Loaded">
    <BeginStoryboard Storyboard="{StaticResource ShakeStoryboard}" />
  </EventTrigger>
</Window.Triggers>

ウィンドウのロード完了と同時にストーリーボードを開始するコード例(XAML)

 標準で提供されているトリガーには、主に、以下のようなものがある。

  • EventTrigger: ルーティング・イベントが発生したときにアクションを実行する。
  • DataTrigger: 指定したプロパティの値が変化したときにアクションを実行する。
  • MultiDataTrigger: 複数の値をトリガーにしてアクションを実行する。

 また、アクション(=トリガーがかかったときに実行される内容)の代表的なものとしては以下のようなものがある。

  • BeginStoryboard: ストーリーボードを開始する。
  • PauseStoryboard: ストーリーボードを一時停止する。
  • SoundPlayerAction: 音声を再生する。

 ちなみに、Expression Blendでは、このTriggersプロパティを使った仕組みではなく、次節で説明するビヘイビアという仕組みを使ってトリガーを掛けることになる。

■ビヘイビアとインタラクション・トリガー

 Movie 3やMovie 4では、ストーリーボード開始のトリガーを設定したり、マルチタッチに応じて回転・拡大・平行移動を掛けたりするために、「ビヘイビアー」と書かれたパネルから何かをUI要素にドラッグ&ドロップしていることが見て取れるだろう。

 このパネルに並んでいるものは、「トリガー」や「ビヘイビア(behavior: 振る舞い)」と呼ばれるものである。トリガーは、WPF標準機能にも同名で、機能的にも同様のものがあるが、Expression Blend付属のライブラリではWPF標準のものよりも少し機能が多い、別の「トリガー」が実装されている。WPF標準のものと区別するために、ここではBlend付属のトリガーを、便宜上、「インタラクション・トリガー」と呼ぶことにする(トリガーが定義されているクラスの名前が「Interaction」であるため)。

インタラクション・トリガー

 インタラクション・トリガーは、もともと、FrameworkElementクラスにTriggersプロパティがないSilverlightにおいて利用するために、添付プロパティとしてトリガーの仕組みを実装し直したものだが、WPFにも逆輸入されている。

 Expression Blendを利用する場合にはWPFでも(Triggersプロパティではなく)インタラクション・トリガーを使うことになるし、そうでない場合にも、こちらを使えばSilverlightへの移植が楽になるだろう。

 また、WPFの標準のトリガー(=Triggersプロパティ)を使ったものよりも、Expression Blend SDK付属のインタラクション・トリガーの方が多彩なトリガーを持っている。例えば、以下のようなトリガーがある。

  • TimerTrigger: タイマーに基づいてアクションを実行する。
  • StoryboardCompletedTrigger: ストーリーボードの完了をトリガーとしてアクションを実行する。

 同様に、アクションの種類も増えている。一例を挙げると以下のとおりである。

  • ChangePropertyAction: プロパティの値を変更する。
  • NavigateToPageAction: ページ間のナビゲーションを指定する。
  • GoToStateAction: 後述するビジュアル・ステート(=外観状態)を変化させる。
  • InvokeCommandAction: コマンドを実行する。

ビヘイビア

 一方、ビヘイビアは、名前どおり、UI要素の振る舞いをXAMLコードとして記述できるように(=Expression Blendのようなツールを使ってドラッグ&ドロップ開発できるように)カプセル化したものである。例えば、Blend標準では以下のようなものがある。

  • FluidMoveBehavior: ウィンドウ・サイズの変更などでUI要素のレイアウトが変化した際に、UI要素が滑らかに移動するようになる。
  • MouseDragElementBehavior: マウス・ドラッグでUI要素を移動できるようになる。
  • TranslateZoomRotateBehavior: マルチタッチ操作イベントに応じてUI要素を回転・拡大・平行移動できるようになる。

 FluidMoveBehaviorおよびMouseDragElementBehaviorの利用例をそれぞれMovie 5およびMovie 6に示す。



ビヘイビアの自作

 Behaviorクラス(System.Windows.Interactivity名前空間)を継承したクラスを作ることで、ビヘイビアの自作も可能だ。

 例えば、日本の業務アプリケーションではよくある要件だが、[Tab]キーの代わりに[Enter]キーでフォーカス移動することを考えてみよう。List 2に示すようなコードで、このような動作をするビヘイビアを作ることができる。List 3にこのビヘイビアの利用例を示す。

using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows;

namespace atmarkit10
{
  public class FocusMoveBehavior : Behavior<UIElement>
  {
    public Key Key { get; set; }
 
    protected override void OnAttached()
    {
      this.AssociatedObject.PreviewKeyDown += AssociatedObject_KeyDown;
    }
 
    protected override void OnDetaching()
    {
      this.AssociatedObject.PreviewKeyDown -= AssociatedObject_KeyDown;
    }
 
    void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
    {
      if ((Keyboard.Modifiers == ModifierKeys.None) && (e.Key == this.Key))
      {
        var element = e.OriginalSource as UIElement;
        element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
      }
    }
  }
}

Imports System.Windows.Input
Imports System.Windows.Interactivity
Imports System.Windows

Namespace atmarkit10

  Public Class FocusMoveBehavior
    Inherits Behavior(Of UIElement)

    Public Property Key() As Key

    Protected Overrides Sub OnAttached()
      AddHandler Me.AssociatedObject.PreviewKeyDown, AddressOf AssociatedObject_KeyDown
    End Sub

    Protected Overrides Sub OnDetaching()
      AddHandler Me.AssociatedObject.PreviewKeyDown, AddressOf AssociatedObject_KeyDown
    End Sub

    Sub AssociatedObject_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
      If ((Keyboard.Modifiers = ModifierKeys.None) AndAlso (e.Key = Me.Key)) Then
        Dim element = CType(e.OriginalSource, UIElement)
        element.MoveFocus(New TraversalRequest(FocusNavigationDirection.Next))
      End If
    End Sub

  End Class

End Namespace

List 2: 任意のキーでフォーカス移動するためのビヘイビア(上:C#、下:VB)

<Grid>
  <i:Interaction.Behaviors>
    <l:FocusMoveBehavior Key="Enter" />
  </i:Interaction.Behaviors>
  <Grid.RowDefinitions>
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="auto" />
    <ColumnDefinition Width="*" />
  </Grid.ColumnDefinitions>
   
  <TextBlock Text="A" />
  <TextBox Grid.Column="1" />
 
  <TextBlock Text="B" Grid.Row="1" />
  <TextBox Grid.Row="1" Grid.Column="1" />

  <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.ColumnSpan="2">
    <Button Content="OK" />
    <Button Content="Cancel" />
  </StackPanel>
</Grid>

List 3: List 2のビヘイビアの利用例(XAML)

【コラム】インタラクション・トリガーとビヘイビアを利用する際の注意点

 インタラクション・トリガーとビヘイビアは、いまのところ.NET Frameworkの標準ライブラリには含まれておらず、Expression BlendもしくはExpression Blend SDKのインストールが必須となる。ただし、関連するクラスの名前空間がSystem名前空間の下にあることから、将来的には.NET Frameworkの標準ライブラリに取り込まれることが期待される。


■外観状態管理(VisualStateManager)

 Silverlightからの逆輸入品として、もう1つ、VisualStateManagerクラス(System.Windows名前空間)というものがある。直訳すれば、「外観状態の管理者」ということになるが、これは、以下のようなボタンの例を考えてみると分かりやすいだろう。

  • ユーザーが操作するマウス・ポインタの動きに応じて、MouseOverやPressedというような状態になる
  • 状態ごとに異なる見た目を持つ(例えば、Pressedのときには暗めの色に変化する)
  • 状態間の遷移の際には0.5秒程度のアニメーションで滑らかに見た目が変化する

 このような、ユーザーの操作に応じた状態の変化、状態ごとの見た目、状態間の遷移を管理するためのクラスがVisualStateManagerクラスである。

 WPF 4では、Buttonクラスなどの標準コントロールは、マウス・ポインタを重ねればMouseOver状態になるなど、VisualStateManagerに最初から対応している。Expression Blendを利用する場合、Movie 7に示すように、コントロール・テンプレートを作ると、MouseOverやPressedなどの状態一覧が表示されるので、見た目の編集が行いやすくなっている。


 コントロール内で定義済みの外観状態だけでなく、Movie 8に示すように、任意の名前の外観状態を定義することもできる。この場合、Expression Blendを使うなら、前述のインタラクション・トリガーとGoToStateActionを利用して状態を遷移させるとよいだろう。



 今回の解説で使用したサンプル・コードは下記のリンク先からダウンロードできる。

 次回はタスク・バー統合などのWindows 7機能や、WPF Toolkitなどの追加提供ライブラリについて説明する。

「連載:WPF入門」のインデックス

連載:WPF入門

前のページへ 1|2       

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。