Storyboard の値をコードで動的に変更する

引き続き、
Windows 8 Store apps Advent Calendar : ATND
http://atnd.org/events/33803
の16日目のネタです。

ちょっと、人数が足りないようなので、じゃあ「タイマーを使わない時計アプリの紹介を」ってのを思いついたので立候補したのですが…

アナログ時計を作ろう! (プログラミングの知識がある方向け)
http://technet.microsoft.com/ja-jp/subscriptions/hh524813.aspx

にすでにあるじゃん orz ってことで、自己却下。storyboard を使うと、プログラム内でタイミング制御をしないから楽なんですよね、ってのと、(たぶん)グラボの性能を引き出せるからプログラム内で制御するよりも軽くなるのでは?ってな想像です。

で、仕方がないので、手元の原稿から「Visual Studio 2012 でスナップ対応の画面を作る」ってネタを出そうと思ったのですが、

WinRT/Metro TIPS:ビューの切り替えを実装するには?[Win 8] – @IT
http://www.atmarkit.co.jp/ait/articles/1209/27/news139.html

ってのに、すでに詳しいページがあってですね。ええ、これで十分だからまあ良いかと…。LayoutAwarePage クラスを継承する「基本ページ」に切り替えておくと、VisualStateManager が、XAML の中のコードから引き出してくれる、ってのと、Visual Studio 2101 と Blend には、これを作成するための特別な「デバイス」表示があって、そこで「状態記録の有効化」をしておくと、見た目通りに作れて便利。ってな具合なのです。

で、ネタかぶりってことで、storyboard の値をプログラム内で、動的に変えるって技…というかノウハウを紹介します。

麻雀パイコントロールを作ったときに悩んだのが、「捨て牌」の動作です。「一萬」を捨ているときと、左端の「1筒」を捨てる時では、捨てる開始位置が違うわけで、これそれぞれに storyboard を作らないとだめなのか…と最初、うんざりしてしまました。まあ、Blend でちまちまと 13 牌分作ってもよいのですが、ちょっとでも場所がずれるとだめとか、そもそも自牌コントロールとして、13 牌はまとめてつくってあるわけで、捨てる先の河の位置を考えると、ちまちま作るのはダメなのかと。

で、次に考えたのが、このあたりの storyboard をプログラム内で作る方法です。
捨て牌のアニメーションを blend で作って後に、ちまちまと C# のプログラムコードに直して、Storyboard を起動させればよいかと。

しかしですね、

<Storyboard x:Name="sbAnimePai">
	<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="animeImagePai">
		<EasingDoubleKeyFrame x:Name="startPosX"  KeyTime="0" Value="892.537">
			<EasingDoubleKeyFrame.EasingFunction>
				<BackEase EasingMode="EaseInOut"/>
			</EasingDoubleKeyFrame.EasingFunction>
		</EasingDoubleKeyFrame>
		<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0">
			<EasingDoubleKeyFrame.EasingFunction>
				<BackEase EasingMode="EaseInOut"/>
			</EasingDoubleKeyFrame.EasingFunction>
		</EasingDoubleKeyFrame>
	</DoubleAnimationUsingKeyFrames>
	<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="animeImagePai">
        <EasingDoubleKeyFrame x:Name="startPosY"  KeyTime="0" Value="458.208">
			<EasingDoubleKeyFrame.EasingFunction>
				<BackEase EasingMode="EaseInOut"/>
			</EasingDoubleKeyFrame.EasingFunction>
		</EasingDoubleKeyFrame>
		<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0">
			<EasingDoubleKeyFrame.EasingFunction>
				<BackEase EasingMode="EaseInOut"/>
			</EasingDoubleKeyFrame.EasingFunction>
		</EasingDoubleKeyFrame>
	</DoubleAnimationUsingKeyFrames>
	<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="animeImagePai">
		<EasingDoubleKeyFrame KeyTime="0" Value="-360">
			<EasingDoubleKeyFrame.EasingFunction>
				<BackEase EasingMode="EaseInOut"/>
			</EasingDoubleKeyFrame.EasingFunction>
		</EasingDoubleKeyFrame>
		<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0">
			<EasingDoubleKeyFrame.EasingFunction>
				<BackEase EasingMode="EaseInOut"/>
			</EasingDoubleKeyFrame.EasingFunction>
		</EasingDoubleKeyFrame>
	</DoubleAnimationUsingKeyFrames>
</Storyboard>

動きをチェックしながら作った、storyboard を手作業で C# のプログラムコードに直すは、結構手間です。しかも、Blend で回転やら移動やら BackEase やらと、いろいろなエフェクトを加えたので、なにがなにやら…って具合。しかも、プログラムのコードに落とした後、さらに Blend で直した日には、もう一度プログラムコードを手作業で直さないといけない、という辛い作業が待っています。これじゃあ、とても手軽に storyboard を使う、とは言えませんね。

で、最終的にどうしたのかというと、結果的には簡単でした。
Storyboard の変更したい箇所に「名前」(x:Name)を付ければ良かったのです。先の XAML をよくみると、「x:Name=”startPosX”」と「x:Name=”startPosY”」というふたつの名前がついてあります。自分の牌から出す場所が変更になるので、開始位置だけをプログラムで変更できれば良いのです。

XAML コントロールに名前を付けられるのは普通なのですが、この手の storyboard の要素にも名前が付けられるし、それをコードから参照できます。と言いますか、XAML コードのどの要素についても名前が付けられるわけですね、なるほど。

private void OnPaiToKawa(double x, double y, ImageSource image)
{
	// 移動開始位置を設定
    x += Canvas.GetLeft(myPai);
    y += Canvas.GetTop(myPai) ;
    startPosX.Value = x;
    startPosY.Value = y;
	// 牌の画像を設定
    animeImagePai.Source = image;
	// アニメーション開始
    sbAnimePai.Begin();
}

そんな訳で、開始位置を指定するだけになるので、プログラムコードは簡単になります。
Blend で作った Storyboard もそのまま活用できます、という両方の良いところが使えるようになります。

storyboard でアニメーションを作ったときの手順としては、

  1. 大まかな動きを Blend で作成する。
  2. 開始位置など、プログラムで制御したい部分に名前を付けておく。
  3. 再び、Blend で細かいアニメーションを追加していく。

という具合です。

こんな風に、Blend の Storyboard とプログラムコードの直書き(というか値の変更)ができますよ、っていう話です。
本来は、この storyboard 部分を別リソースにしたいんですけどね。このあたりは調査中。

カテゴリー: 雑談 パーマリンク