M-V-VMパターンについてのエントリを訳してみた 原題:”WPF patterns : MVC, MVP or MVVM or…?”
最近WPFアプリケーションの設計で頭を悩ませていまして、どのような設計をしたものかと色々と記事を物色しているのですが、どうやらModel-View-ViewModelパターンというのが良いみたいですね。そこでとりあえずThe Orbifoldというサイトの、WPF patterns : MVC, MVP or MVVM or…?というエントリを勉強がてら訳しながら読んでみました。自分も英語が得意なわけではないので、ミスがありましたらご指摘をお願いします。
2009-02-21追記:もう一つ記事を訳してみました:M-V-VMパターンについてのエントリを訳してみた2 原題:「David Hill’s WebLog : The ViewModel Pattern」 – SharpLab.
―訳ここから―
導入 Introduction
XAMLの登場以来、WindowsアプリケーションのためにMVCアーキテクチャを概念化を試みる上で、事態は少しややこしくなりました。ウェブとデスクトップのギャップは狭まってきており、WPF全体が様々な可能性を道具箱に加えました。使うべきものは?model-view-controlerd?それともmodel-view-presenter?model-view-viewmodelと呼ばれる新しいパラダイムでしょうか?
Since XAML things have become a bit complicated in trying to conceptualize MVC architectures for Windows applications. The gap between web and win is narrowing and the whole WPF thing adds diverse possibilities to one’s toobox. What to use? Model-view-controler, the model-view-presenter or the new paradigm called model-view-viewmodel?
私は一体全体それが何なのかを理解することを試みました。そしてこれは私が見つけ出したものです。
I have tried to understand what it’s all about and this is what I found out.
何が問題なのか? What’s the problem?
WPFは幾つかのことを変化させました。
WPF has changed a few things:
- 宣言型プログラミング(即ちXAML)によって、特にデータバインディングにおいて、たくさんの方法が用意され、多くのコードが節約されました。別の分野は、例えば、アニメーションとスタイリングです。以前はC#でプログラムされる必要があったそれらが、今ではXAMLの中でトリガとイベントで可能です。
- MVCパターンにおいて、ViewはModelからのデータを表示していますが、今やXAMLがこの仕事を行い、時々、表示を更新するのにValueConverterやObservableCollectionのようなものを必要とします。
- WPFにおけるユーザーコントロールはアプリケーション内部で使われるときにオーバーライドされることができるデフォルトのgeneric.xamlスタイルを定義することが出来、即ち既存のコントロールへのスタイル付けはModelの表示に柔軟性を加える新しい機能です。
- 新しく追加されたICommand、XAMLのトリガ、アニメーション、イベントのバブリング、トンネリングを通じて、XAMLは物事を変更するため、ユーザーのアクションに応えるための様々なメカニズムを可能にします。
- through declarative programming (i.e. XAML) a lot of plumbing is created for you which especially in the context of databinding saves a lot of code. Another area is e.g. animations and styling; what previously had to be programmed in C# is now possible inside XAML through triggers or events.
- in the MVC pattern the View is presenting the data from the Model but now XAML does this job and sometimes need things like value converters or the ObservableCollection to update the presentation
- user controls in WPF allow you to define a default generic.xaml style which can be overriden when used inside an application, i.e. styling of existing controls is a new feature which adds flexibility to the presentation of the Model
- XAML allows a wide variety of mechanisms to change things or to react to user actions; through the ICommand that is now defined in the framework, through triggers in XAML, through animations, through event bubbling or tunneling (which is also new)
MVVMアーキテクチャ The MVVM architecture
概観 Overview

DataModel
DataModelはWPFによって使いやすいようにデータを公開する責任を負います。そのすべてのPublicなAPIはUIスレッドにおいてのみで呼ばれる必要があります。INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを適切に実装する必要があります。データを取ってくるコストが高い場合は、コストの高い操作を取り去り、UIスレッドを決してブロックしないようにします。ブロックすることは悪です!同様にデータを新鮮に保ち、複数のソースからのデータを組み合わせるのにも使えます。これらのクラスは容易にユニットテスト出来ます。
DataModel is responsible for exposing data in a way that is easily consumable by WPF. All of its public APIs must be called on the UI thread only. It must implement INotifyPropertyChanged and/or INotifyCollectionChanged as appropriate. When data is expensive to fetch, it abstracts away the expensive operations, never blocking the UI thread (that is evil!). It also keeps the data “live” and can be used to combine data from multiple sources. These sorts of classes are fairly straightforward to unit test.
ViewModel
ViewModelはアプリケーションにおけるViewのためのModelです(当然ですが)。Viewに関係のあるデータを公開し、通常Commandsと共にViewのためのbehaviorを公開します。ModelはアプリケーションのViewに対して極めて特化していますが、いずれのWPFクラスのサブクラスでもなく、バインドされるUIについての想定もしません。ViewModelは実際のUIから分離されているので、これらのクラスは同様に比較的容易にユニットテストできます。
A ViewModel is a model for a view in the application (duh!). It exposes data relevant to the view and exposes the behaviors for the views, usually with Commands. The model is fairly specific to a view in the application, but does not subclass from any WPF classes or make assumptions about the UI that will be bound to it. Since they are separate from the actual UI, these classes are also relatively straightforward to unit test.
View
Viewはアプリケーションのビューの後ろにある実際のUIです。我々が使うパターンはそのViewModelにviewのDataContextをセットする必要があります。これがバインディングを通じてViewModelに到達するのを容易にします。また、WPFのDataTemplate/Data patternにも合致します。理想を言えば、Viewはコードビハインド無しのXAMLだけで実装できるでしょう。添付プロパティはこれに非常に役に立つでしょう。
A View is the actual UI behind a view in the application. The pattern we use is to set the DataContext of a view to its ViewModel. This makes it easy to get to the ViewModel through binding. It also matches the DataTemplate/Data pattern of WPF. Ideally, the view can be implemented purely as Xaml with no code behind. The attached property trick comes in very handy for this.
DataModelとViewModelの間の線引きはあいまいになりがちです。DataModelは時折DataTemplateと共に表示され、それは我々がViewModelを使うやり方と殆ど変わりません。しかしながら、実際にはその区別は通常意味があります。私は同様に多くのレイヤでしばしば合成が行われることも指摘しなければなりません。ViewModelは他のViewModelやDataModelを構成するかもしれません。また、DataModelはほかのDataModelによって構成されるかもしれません。
The lines between DataModels and ViewModels can be blurry. DataModels are often shown in the UI with some DataTemplate, which isn’t really so different than the way we use ViewModels. However, the distinction usually makes sense in practice. I also want to point out that there’s often composition at many layers. ViewModels may compose other ViewModels and DataModels. And, DataModels may be composed of other DataModels.
ViewModelはViewのModelです。DataObject(Model)のプロパティをViewObject(View)のプロパティにデータバインドしたいけれども、たまに、変換や計算がために、ModelのCLRプロパティを直接バインドすることができないことがあります。そこでViewModelの出番です。ViewModelは計算済み、変換済みのModelからの値を公開するので、このプロパティをViewのプロパティに直接バインドすることが出来るようになります。
The ViewModel is a model of the view. That means: You want to DataBind a property from your DataObject (model) to a property from your ViewObject (view) but you sometimes cannot bind directly to a CLR property of the model (because of converting or calculating). This is when ViewModel comes into play. It propagates the already calculated or converted value from your model, so you can bind this property directly to the view property.
Model-View-ViewModelアーキテクチャの一番の要点はデータ(Model)の上に、データのコンセプトをデータのビュー(View)のコンセプトにより近付けてマッピングする非視覚的なコンポーネントの別のレイヤ(ViewModel)があることのようです。Viewがバインドする先はViewModelであり、Modelに直接バインドするのではありません。
The main thrust of the Model/View/ViewModel architecture seems to be that on top of the data (”the Model”), there’s another layer of non-visual components (”the ViewModel”) that map the concepts of the data more closely to the concepts of the view of the data (”the View”). It’s the ViewModel that the View binds to, not the Model directly.
The model
INotifyPropertyChangedを使うことで、変更をスタックをバブリングさせて伝えることができます。UIスレッドにプロパティの変化を分離スレッドから処理させるメソッドがあるにも関わらず、PublicなメソッドがUIスレッドにあるべき理由は、ModelはUIをブロックしうる、時間のかかる、或いは非同期のを呼び出すこともあり得るからです。詳しくはDispatcherオブジェクトとWPFスレッドモデルのドキュメントを参照ください。DispatcherのBeginInvoke()メソッドによってバックグラウンドで実行する場合は優先度を指定するパラメータに気を付けてください。Dispatcherがbusyでないとき、SystemIdleは特に興味深いです。
Using the INotifyPropertyChanged you can bubble changes up the stack. The reason that public methods should be on the UI thread is because the model could call long running or async stuff which would block the UI, though there are methods to let the UI thread handle property changes from a separate thread. See the doc on the Dispatcher object and the WPF threading model for more on this. Note in this context that you can let things happen in the background by means of the BeginInvoke() method of the Dispatcher and the paramter that specifies the priority. The SystemIdle in particular is interesting to be used when the Dispatcher is not busy.
downloadにあるDataModelはDan Crevierのサンプルの真似で、Modelの抽象基本クラスとして役立つでしょう。
The DataModel you can find in the download is mimiced from the Dan Crevier’s sample and can serve as an abstract base class for your own models.
Dispatcher things
DispatcherTimerはDispatcherループの最初毎に再評価されます。
The DispatcherTimer is reevaluated at the top of every Dispatcher loop.
Timerは時間間隔に正確なタイミングで実行されることが保証されていませんが、時間間隔より前に実行されないことは保証されています。これはDispatcherTimer操作が他の操作と同様にDispatcherキューに配置されているからです。DispatcherTimer操作が実行されるタイミングはキューにある別のジョブとその優先度に依存します。
Timers are not guaranteed to execute exactly when the time interval occurs, but are guaranteed to not execute before the time interval occurs. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes is dependent on the other jobs in the queue and their priorities.
WPFアプリケーションでSystem.Timers.Timerが使われた場合、System.Timers.TimerがUIスレッドとは別のスレッドで実行されることは注目すべきことです。UIスレッドのオブジェクトにアクセスするにはInvokeかBeginInvokeを使ってUIスレッドのDispatcherに操作を渡す必要があります。System.Timers.Timerの例としては、System Timer sampleのthe Disable Command Sourceを参照してください。System.Timers.TimerではなくDispatcherTimerを使う理由はDispatcherTimerがDispatcherと同じスレッドで実行され、DispatcherTimer上でDispatcherPriorityがセットされうるからです。
If a System.Timers.Timer is used in a WPF application, it is worth noting that the System.Timers.Timer runs on a different thread then the user interface (UI) thread. In order to access objects on the user interface (UI) thread, it is necessary to post the operation onto the Dispatcher of the user interface (UI) thread using Invoke or BeginInvoke. For an example of using a System.Timers.Timer, see the Disable Command Source Via System Timer sample. Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer.
DispatcherTimerは何時でもオブジェクトのメソッドがタイマにバインドされた時はオブジェクトを生かし続けます。
A DispatcherTimer will keep an object alive whenever the object’s methods are bound to the timer.
それで、WPFのUIの中で物事をスケジュールする正しいやり方はこのようになります。
So, the right way to schedule things inside the WPF UI is something like;
private DispatcherTimer _timer;
timer = new DispatcherTimer(DispatcherPriority.Background);
timer.Interval = TimeSpan.FromMinutes(5);
timer.Tick += delegate { ScheduleUpdate(); };
timer.Start();
タイマはUIのdispatcherに結びついたスレッドに暗黙的に挿入されます。
the timer is injected implicitly in the thread associated to the dispatcher of the UI.
References
- http://blogs.sqlxml.org/bryantlikes/archive/2006/09/27/WPF-Patterns.aspx
- http://blogs.msdn.com/johngossman/default.aspx
- http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx
- http://questmaster.net/blogs/dirks_blog/archive/2005/10/11/237.aspx
- Threading issues and WPF data binding is well explained in this post of Beatriz Costa
―訳おわり―
DispatcherTimerってM-V-VMパターンとどう関係してくるんだろう?
M-V-VMパターンについての具体的な記事を見たい場合は、M-V-VM « Karl On WPF – .NetというページにM-V-VMについて書かれた記事が纏まっているので、これも参照すると良いかもしれません。また、Patterns: WPF Apps With The Model-View-ViewModel Design Patternという凄く面白そうな記事がMSDN Magazineの2009-02号に出ているので、少し待てばこれの日本語訳も出てくるでしょう。ちなみにこのMSDN Magagineの記事の著者はCrack.NETを作られた方でもあるようで、Crack.NETはM-V-VMパターンの良いサンプルコードになっているらしいです。
誰得?Google Latitude