M-V-VMパターンとアニメーションをどのようにして組み合わせるのかは、MVVMパターンを使う上で常に頭を悩ませる問題です。この問題に対して、Josh SmithさんがOrchestrating Animated Transitions between View and ViewModel >> Josh Smith on WPFというエントリでひとつの方法を示していたので、今回はこれを訳してみました。

Introduction

Model-View-ViewModel(MVVM)パターンを使う場合、Viewに表示されるデータはほぼ常にデータバインディングを通じてやり取りされます。ViewModelオブジェクトのプロパティに対する変更はすぐにViewのバインディングターゲットに対して伝達されます。これは普通は良いことです。しかしながら、時によっては、伝達が即時に行われることが問題になることがあります。

When working with the Model-View-ViewModel (MVVM) pattern, the data shown in a View is almost always transferred to it via data binding.  Changes made to properties on a ViewModel object immediately propagate to the binding targets in the View.  This is normally a good thing.  Sometimes, however, the immediacy of the propagation can be a problem.

なぜ新しいプロパティの値がUIに即座には現われて欲しくないと思うのでしょうか?では、古い値と新しい値との間でのアニメーション化された遷移を表示したい場合を考えてみてください。新しい値が即座にViewに表示された場合、新しい値を”アニメートイン”する前に古い値を”アニメートアウト”するチャンスがありません。例えば、プロパティの値がリストとして表示される1ページのデータ項目だと考えてみてください。新しい項目のページをフェードする前に最初のページのアイテムをフェードアウトしたい場合、新しい項目のページが表示される前に、フェードアウトアニメーションを始める必要があります。

Why would you want a new property value to not immediately show up in the UI?  Well, suppose you want to display an animated transition between the old value and the new value.  If the new value immediately shows up in the View, you won’t have a chance to animate “out” the old value before animating “in” the new value.   For example, suppose the property’s value is a page of data items to show in a list.  If we want to fade away the first page of items before fading in the new page of items, we need to start the fade-away animations before the new page of items is displayed.

これは、Visual State Manager(VSM)が解決する種類の問題です。VSMは未だ正式なWPFの一部になっておらず(.NET 4.0まではなりません)、我々は未だそれをアプリケーション作成に役立てることはできません。また、VSMはカスタムコントロールの中で使われるために設計されているので、私達がこれから探そうというものとは完全に同じという訳ではありません。

This is the type of problem that the Visual State Manager (VSM) solves.  Since the VSM is not yet part of WPF proper (it won’t be until .NET 4.0), we cannot make use of it yet in production applications.  Also, the VSM is designed to be used inside of custom controls, so it is not exactly equivalent to what we are going to explore below.

The AnimatedTransition Class

私はWPFやSilverlightのアプリケーションに取り組んでいる際に、まさにこの問題に何回も直面しました。プロパティの変化をアニメーション化された遷移と協調させるための様々なアプローチを試したあと、私は核となる問題を、より鮮明に理解し始めました。昨日私はScherzo from Mendelssohn’s “A Midsummer’s Night Dream”のこのとてつもなく素晴らしい演奏を聴き、そしてパッと閃きました。私は二つのプロパティ値間でのアニメーション化された遷移を作成するための、ViewModelとViewをして協調させるシンプルで、タイプセーフで、再利用可能な方法を悟りました。AnimatedTransitionクラスが生まれたのです!

I faced this exact problem many times while working on WPF and Silverlight applications.  After experimenting with several approaches to coordinating property changes with animated transitions, I started to understand the core problem more clearly.   Yesterday I listened to this incredible rendition of the Scherzo from Mendelssohn’s “A Midsummer’s Night Dream” and was hit by a flash of inspiration.  I realized a way to create a simple, type-safe, reusable way of having a ViewModel and View work together to create animated transitions between two property values.  The AnimatedTransition class was born!

image

AnimatedTransitionのインスタンスはどのViewModelクラスにでも埋め込むことが出来ます。遷移を行う必要のあるViewは、遷移を何時始めるべきかを知るために、AnimatedTransitionオブジェクトにアクセスし、そのイベントの一つ以上をフックする必要があります。

An instance of AnimatedTransition can be embedded in any ViewModel class.  The View(s) that need to perform transitions must access the AnimatedTransition object and hook one or more of its events in order to know when transitions should start.

The Demo App

このエントリの最後に載せたデモアプリでは、以下のように「ページ」毎に四つのハローキティを表示するItemsControlがあります。

In the demo app, which is available at the bottom of this post, there is an ItemsControl that displays four Hello Kitty images per “page,” as seen below…

image

ユーザ―が下の四角を通じて別のページに遷移する際に、古いキティがフェードアウトし、新しいのがフェードインします。

When the user navigates to a different page, via the circles below, the old kitties fade away and then the new ones fade in.

この小さいアプリを動かすのに関係している全てのクラスを精査していくことはしません。ViewModelクラスについてしっかりと理解したい場合は、このクラスダイアグラムをクリックしてフルサイズのを見てください。

We are not going to examine all of the classes involved in making this little app work.  If you want to get a high-level understanding of the ViewModel classes, click on this class diagram to view it at full size:

image

ItemsControlに表示されるキティの現在のページはKittyKatTerraceViewModelのSelectedPageプロパティに含まれます。あのプロパティの二つの値の間での遷移を必要とする場合、我々はAnimatedTransitionを埋め込んで使います。KittyKatTerraceViewModelがAnimatedTransitionを使用し、公開する方法はこのようになっています:

The current page of kitties to show in the ItemsControl is contained in SelectedPage property of KittyKatTerraceViewModel.  Since we need to have a transition between the two values of that property, we use an AnimatedTransition to enable that.  Here is how KittyKatTerraceViewModel uses and exposes an AnimatedTransition:

public AnimatedTransition<PageOfKittyKatsViewModel> GetSelectedPageTransition()
{
    if (_selectedPageTransition == null)
        _selectedPageTransition =
            new AnimatedTransition<PageOfKittyKatsViewModel>(page => this.SelectedPage = page);

    return _selectedPageTransition;
}
public PageOfKittyKatsViewModel SelectedPage
{
    get { return _selectedPage; }
    private set
    {
        if (value == _selectedPage)
            return;

        _selectedPage = value;

        foreach (var page in this.Pages)
            page.RefreshSelectionState();

        this.OnPropertyChanged("SelectedPage");
    }
}

ユーザーがページを選択するためにItemsControlの真下の小さい四角をクリックしたとき、PageOfKittyKatsViewModelクラスのこのメソッドが実行されます:

When the user clicks on a little circle beneath the ItemsControl to select a page, this method in the PageOfKittyKatsViewModel class executes:

void Select()
{
	// Tell the KittyKatTerraceViewModel to begin
	// a transition of the SelectedPage property
	// so that it references this page.
    _owner.GetSelectedPageTransition().Start(this);
}

KittyKatTerraceViewコントロールのコードビハインドにある全ての関連するロジックを以下にリストアップします。これはViewがどうやってAnimatedTransitionオブジェクトを使用し、相互作用しているかを示しています。

All of the relevant logic in the KittyKatTerraceView control’s code-behind is listed below.  This shows how a View can consume and interact with an AnimatedTransition object.

void OnItemsHostPanelLoaded(object sender, RoutedEventArgs e)
{
    _itemsHost = sender as Panel;
}

void OnLoaded(object sender, RoutedEventArgs e)
{
    var viewModel = base.DataContext as KittyKatTerraceViewModel;

    viewModel.SetKittyLocations(new List<Point>
    {
        new Point(255, 65),
        new Point(115, 145),
        new Point(280, 225),
        new Point(135, 340)
    });

    _transition = viewModel.GetSelectedPageTransition();
    _transition.BeforeApplyNewValue += this.OnBeforeNewSelectedPage;
    _transition.AfterApplyNewValue += this.OnAfterNewSelectedPage;
}

void OnBeforeNewSelectedPage(object sender, EventArgs e)
{
    Storyboard sb = this.CreateStoryboard("FadeOutAnim");
    sb.Completed += delegate { _transition.Finish(); };
    sb.Begin();
}

void OnAfterNewSelectedPage(object sender, EventArgs e)
{
    // Wait until the panel has new children before animating.
    _itemsHost.LayoutUpdated += this.OnItemsHostLayoutUpdated;
}

void OnItemsHostLayoutUpdated(object sender, EventArgs e)
{
    _itemsHost.LayoutUpdated -= this.OnItemsHostLayoutUpdated;
    this.CreateStoryboard("FadeInAnim").Begin();
}

古いアイテムをフェードアウトさせるStoryboardのCompletedイベントにハンドラをアタッチする必要があることによく気を付けて下さい。そのイベントが発生したとき、新しい値への遷移を終わらせるために、AnimatedTransitionは発生を伝えられる必要があり、それをうけて実際にその値を設定します。それはFinish()メソッドを呼ぶことによって実現されます。一度新しい値が設定されたならば、AfterApplyNewValueイベントが発生し、その際あなたは新しいアイテムにフェードインすることが出来ます。

It’s important to note that you must attach a handler to the Completed event of the Storyboard that fades away the old items.  When the event is raised, the AnimatedTransition must be told to finish transitioning to the new value, and actually apply the value.  That is accomplished by calling the Finish() method.  Once the new value has been applied, the AfterApplyNewValue event is raised, at which time you can fade in the new items.

ソースコードはここからダウンロードすることが出来ます。注意:ファイルの拡張子を.DOCから.ZIPに変更して解凍して下さい。

You can download the source code here.  Note: Be sure to change the file extension from .DOC to .ZIP and then decompress it.

Trackbacks : 2

Trackback URL for this entry
http://blog.sharplab.net/blog/2009/04/29/%e3%80%90%e7%bf%bb%e8%a8%b3%e3%80%91view%e3%81%a8viewmodel%e9%96%93%e3%81%a7%e3%82%a2%e3%83%8b%e3%83%a1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e5%8c%96%e3%81%95%e3%82%8c%e3%81%9f%e9%81%b7%e7%a7%bb/trackback/

Listed below are links to weblogs that reference this entry

トラックバック from diet books 14-05-31 09:06:16 UTC

diet books…

【翻訳】ViewとViewModel間でアニメーション化された遷移を仕立てる~ 原題”Orchestrating Animated Transitions between View and ViewModel” – SharpLab….

トラックバック from How To Lose Weight Fast At Home Yahoo Answers 15-07-21 02:18:57 UTC

How To Lose Weight Fast At Home Yahoo Answers…

… health – It is essential that sufferers consider a waist that did not determine the mechanism of increasing you metabolic rate. 3 of easy meal plan to lose weight fast are overweight and… 【翻訳】ViewとViewModel間でアニメーション化された遷移を仕立てる … ……