2010年5月17日月曜日

番外編 Todoアプリを作ってみた2

http://minitodo.codeplex.com/

ついったークライアントが行き詰まっているので、Todoアプリを更新してみました。
今回はアニメーションの説明でも。
ソースが必要な場合は上のリンクから取ってね。

最初に断っておきますが、MVVMとアニメーションは相性が悪いらしいです。
http://blog.sharplab.net/computer/cprograming/wpf/3065/
の記事とか読んだ限り。
そして今回の私の投稿はMVVMを思いっきり無視して作っています。
むしろ邪道なやり方と言ってもいいかもしれません。
ちゃんとやりたい方は上記のリンク先のやり方とかやってください。

そして本流のついったークライアントではないので、かなり省いて説明します。
実際の動きがイメージできないときはバイナリをDLしてためしてください。

まず、Todoを追加したときに、
右からスライドインしてくるようなアニメーションを作ります。

XAML


       <!--新規作成時のスライドインアニメーション用-->
       <Grid.RenderTransform>
        <TranslateTransform x:Name="_slideInTransform"></TranslateTransform>
       </Grid.RenderTransform>
       <Grid.Resources>
        <!--新規アイテムを挿入するアニメーション-->
        <Storyboard x:Key="_slideInAnimation">
         <DoubleAnimation Storyboard.TargetName="_slideInTransform"
                 Storyboard.TargetProperty="X"
                 From="{Binding ElementName=_window, Path=Width}" Duration="0:0:0.5">
         </DoubleAnimation>
        </Storyboard>

Gridは1つのアイテムを表す入れ物です。(ListBoxItemの直下)
GridにTranslateTransformをつけておいて
(プロパティを付けていないので、おいただけでは何もしない)、
そのTranslateTransformのXの値を5秒間でWidthから0に減らすStoryboardを
Resourceに入れておきます。



  private void Grid_Loaded(object sender, RoutedEventArgs e) {
   var container = sender as Grid;
   var vm = container.Tag as TodoViewModel;//ViewModelを渡す簡単な方法が思いつかないので、Tagに入れている
   var id = vm.Id;
   var animation = container.FindResource("_slideInAnimation") as Storyboard;

   if (!_alreadyAnimated.Contains(id)) {
    animation.Begin();
    _alreadyAnimated.Add(id);
   }
  }

んで、Loadedイベントでアニメーションを開始します。
Loadされた時には2番目以降のアイテムが1行ずつ下がっていますので、
新しいアイテムが本来の位置からXだけ右に表示される(そしてXは徐々に減っていく)というわけです。
ただし、それだけだとすべてのアイテムがLoadされるたびにアニメーションされるので、
新規ではないやつはアニメーションしないようにIDを管理しておきます。
これだけです。
Loadedでやるのがスマートではないですね。

次に完了したらフェードアウトする処理です。


        <Storyboard x:Key="_doneAnimation">
         <DoubleAnimation Storyboard.TargetName="_itemContainer"
              Storyboard.TargetProperty="Opacity"
              To="0" BeginTime="0:0:0.5" Duration="0:0:1"/>
         <DoubleAnimation Storyboard.TargetName="_itemBackground"
              Storyboard.TargetProperty="Opacity"
              From="1" Duration="0:0:0.1"/>

背景(WhiteだけどOpacity=0.01→ほぼ透明)を一瞬だけ白くする(Opacity=1)アニメーションと
アイテム自体を徐々に透明にするアニメーションを入れています。

これもMVVM的にVMのコマンドを直接バインドすると
アニメーションをするまえにListBoxから無くなってしまうので、
Viewのコードビハインドのイベントハンドラで


   var animation = container.FindResource("_doneAnimation") as Storyboard;
   animation.Completed += (s, eArg) => {
    vm.CompleteCommand.Execute(null);
   };
   animation.Begin();

というように、StoryboardのCompletedイベントで
ViewModelの完了コマンドを呼ぶようにしています。
(アニメーションしている間はViewModel的には完了していないということです。)

0 件のコメント:

コメントを投稿