WPFのアニメーションは以下のような特徴を持っている。
●低レベルなアニメーション制御
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>
この例ではストーリーボードのコンテンツであるタイムライン(=<DoubleAnimationUsingKeyFrames>要素など)は1つしかないが、Figure 1に示すように、ストーリーボードは複数のタイムラインを持つことができる。
各タイムラインは、Figure 2に示すように、「どのUI要素のどのプロパティを、どう動かすか」という情報を持っている。動かし方ごとに異なるクラスとなっている。
ちなみに、TargetProperty属性には、データ・バインディングで使うBindingマークアップ拡張のPathプロパティと同様の構文でパスを指定する(第5回参照)。
○キー・フレームを使ったタイムライン
Expression Blendでの編集結果として得られるために最も目にする機会が多くなる、キー・フレームを使ったタイムライン(=型名がUsingKeyFramesで終わっているタイムライン)について、もう少し詳しく説明しておこう。
キー・フレームとは、アニメーションにおける変化の基点となるフレーム(=いわゆる「コマ」)のことをいう。アニメーションは、いわゆるパラパラ漫画の要領で、1フレームごとに位置や色などを指定することでも制御可能である。しかし、この方法には以下のような問題がある。
そこで、WPFでは、とびとびにキー・フレームというものを設定し、位置や色などの情報はキー・フレームに対してだけ行い、その間の値は補間によって生成することでアニメーションを制御している。補間は時刻に応じて行われるため、フレーム・レートが変化してもアニメーションの再生速度は変わらない。 それでは、具体的なキー・フレームの設定方法を見ていこう。WPFでは、、Figure 3に示すように、「どの時刻までにどういう値になっているべきか」と「値の補間方法」を指定する。
図中の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>
標準で提供されているトリガーには、主に、以下のようなものがある。
また、アクション(=トリガーがかかったときに実行される内容)の代表的なものとしては以下のようなものがある。
ちなみに、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付属のインタラクション・トリガーの方が多彩なトリガーを持っている。例えば、以下のようなトリガーがある。
同様に、アクションの種類も増えている。一例を挙げると以下のとおりである。
●ビヘイビア
一方、ビヘイビアは、名前どおり、UI要素の振る舞いをXAMLコードとして記述できるように(=Expression Blendのようなツールを使ってドラッグ&ドロップ開発できるように)カプセル化したものである。例えば、Blend標準では以下のようなものがある。
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
<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>
インタラクション・トリガーとビヘイビアは、いまのところ.NET Frameworkの標準ライブラリには含まれておらず、Expression BlendもしくはExpression Blend SDKのインストールが必須となる。ただし、関連するクラスの名前空間がSystem名前空間の下にあることから、将来的には.NET Frameworkの標準ライブラリに取り込まれることが期待される。
Silverlightからの逆輸入品として、もう1つ、VisualStateManagerクラス(System.Windows名前空間)というものがある。直訳すれば、「外観状態の管理者」ということになるが、これは、以下のようなボタンの例を考えてみると分かりやすいだろう。
このような、ユーザーの操作に応じた状態の変化、状態ごとの見た目、状態間の遷移を管理するためのクラスがVisualStateManagerクラスである。
WPF 4では、Buttonクラスなどの標準コントロールは、マウス・ポインタを重ねればMouseOver状態になるなど、VisualStateManagerに最初から対応している。Expression Blendを利用する場合、Movie 7に示すように、コントロール・テンプレートを作ると、MouseOverやPressedなどの状態一覧が表示されるので、見た目の編集が行いやすくなっている。
コントロール内で定義済みの外観状態だけでなく、Movie 8に示すように、任意の名前の外観状態を定義することもできる。この場合、Expression Blendを使うなら、前述のインタラクション・トリガーとGoToStateActionを利用して状態を遷移させるとよいだろう。
今回の解説で使用したサンプル・コードは下記のリンク先からダウンロードできる。
次回はタスク・バー統合などのWindows 7機能や、WPF Toolkitなどの追加提供ライブラリについて説明する。
「連載:WPF入門」
Copyright© Digital Advantage Corp. All Rights Reserved.