原文
Model View ViewModel (MVVM)パターンは、あなたが、そのユーザー・インターフェイス(UI)から、 あなたのアプリケーションのビジネスとプレゼンテーション・ロジックを、明確に、分離することを支援します。 アプリケーション・ロジックとUIの間で明確な分離を継続することは、数多くの開発と設計の問題に対処するのに役立ちます。 そして、あなたのアプリケーションを、はるかに簡単に、テスト、保持、そして、発展できます。また、それは、コードを再利用する機会をはるかに改善することができます。 そして、開発者とUIデザイナーは、さらに簡単に共同作業することができます。アプリケーションの、それぞれの部品を開発するとき、
MVVMパターンを使用して、アプリケーションUIと根底にあるプレゼンテーションとビジネス・ロジックは、3つの分離したクラスに、分離されます。 :Viewは、UIとUIロジックをカプセル化します。; View Modelは、プレゼンテーション・ロジックと状態をカプセル化します。; そして、モデルは、アプリケーションのビジネス・ロジックとデータをカプセル化します。
]Prismには、サンプルと参考になる実装が含まれています。 それは、Windows Presentation Foundation (WPF)アプリケーションで、MVVMパターンを実装する方法を紹介します。 また、Prismライブラリは、あなた独自のアプリケーションで、パターンを実装するのを助けることができる機能を提供します。 これらの機能は、MVVMパターンを実装するために、最も一般的な慣例を具体的に示します。 そして、テスタビリティをサポートし、Expression BlendとVisual Studioで、うまく動作するように設計されています。
この項目は、MVVMパターンの概要を提供し、その基本的な特徴を実装する方法を説明します。 高度なMVVMの筋書きの項目は、Prismライブラリを使用して、さらに進化したMVVMの筋書きを、どのように実装するかを説明します。
クラスの責任と特徴
Class Responsibilities and Characteristics
MVVMパターンは、中心となるWPF機能の一部を活用するために最適化された、データ結合、データテンプレート、コマンドと動作のような、 プレゼンテーション・モデル・パターンの密接なバリアントです。
MVVMパターンでは、Viewは、UIとどんなUIロジックでもカプセル化します。;View Modelは、プレゼンテーション・ロジックと状態をカプセル化します。; そして、Modelは、ビジネス・ロジックとデータをカプセル化します。ビューは、データ結合、コマンドと変更通知イベントを通じて、View Modelと相互作用します。 View Modelは、ビュー内の表示のために、必要に応じて、Model、変換、検証、そして、集約したデータに、問い合わせ、観察し、更新を調整します。
次の図は、3つのMVVMクラスとそれらの相互作用を示しています。
MVVMクラスとそれらの相互作用
すべての分離されたプレゼンテーション・パターンのように、効果的にMVVMパターンを使用するための鍵は、 あなたのアプリケーションのコードを、適切なクラスの要素として考慮するために、 そして、これらのクラスが、さまざまな筋書きで相互作用する方法を理解するには、適切な方法を理解することにあります。 次の項目では、MVVMパターンのクラスの各々の役割と特徴を説明します。
ビュー・クラス
The View Class
ビューの役割は、ユーザーが画面で見るものの、構造と外観を定義することです。 理想的には、ビューの分離コードには、InitializeComponentメソッドを呼び出すコンストラクタだけが含まれています。 場合によっては、分離コードが、複雑なアニメーションのような、あるいは、コードが、直接、ビューの一部の視覚的な要素を操作する必要があるとき、 Extensible Application Markup Language (XAML)で表現することが、難しい、あるいは、能率が悪い、視覚的な動作を実装する、 UIロジック・コードが含まれているかもしれません。あなたは、あなたが、ユニット・テストが必要となるビューに、どんな論理コードも配置してはいけません。 一般的に、ビューの分離コード内の論理コードは、方法を検証する、UIオートメーションを通して、テストされます。
WPFでは、ビューのデータ結合式は、そのデータ背景に対して評価されます。MVVMでは、ビューのデータの状況は、 View Modelに設定されます。View Modelは、ビューに、プロパティとコマンドの実装を結びつけ、 そして、変更通知イベントを通して、状態のどんな変更でもビューに通知します。 一般的に、ビューとそのView Modelの間に、1対1のリレーションシップがあります。
一般的に、ビューは、コントロールから派生した、あるいは、UserControlから派生したクラスです。 しかしながら、場合によっては、データテンプレートで表されるビューが表示されるとき、 それは、オブジェクトを、視覚的に示すために使用されるUI要素を指定します。データテンプレートを使用して、 ビジュアル・デザイナーは、View Modelが、どのようにレンダリングされるかについて、簡単に指定することができます。 あるいは、基盤となるオブジェクト自体、あるいは、それを表示するために使用されるコントロールの動作を変更することなく、 その既定の視覚的な表現を修正することができます。
データテンプレートは、少しの分離コードも持っていない、ビューと考えることができます。 それらは、UIで表示することを要求されるたびに、特定のView Model型と結合するように設計されています。 実行時に、データテンプレートによって定義されるビューは、自動的にインスタンスを生成され、 対応するView Modelに、そのデータ・コンテクストを設定されるでしょう。
WPFでは、あなたは、アプリケーション階層で、View Model型とデータテンプレートを関連付けることができます。 WPFは、続いて、それらがUIで表示されるたびに、指定された型のどんなView Modelオブジェクトにでも、 自動的に、データテンプレートを適用するでしょう。これは、暗黙のデータ・テンプレートとして知られています。データ・テンプレートは、親ビューの外側で、 それ、あるいは、リソース・ディクショナリを使用する、コントロールと一緒に、インラインで定義することができます。 そして、宣言的に、ビューのリソース・ディクショナリに結合されます。
要約すると、ビューは、次に示す、重要な特徴を持っています。:
- ビューは、ウィンドウ、ページ、ユーザー・コントロール、データテンプレートのような、視覚的な要素です。 ビューは、ビューとそれらの視覚的なレイアウトとスタイルに含まれるコントロールを定義します。
- ビューは、そのDataContextプロパティを通して、View Modelを参照します。 ビューのコントロールは、プロパティへのデータ結合とView Modelで公開される命令です。
- ビューは、ビューとView Modelの間で、データ結合の動作をカスタマイズするかもしれません。 例えば、ビューは、UIで示されるデータをフォーマットするために、値コンバータを使用するかもしれません。 あるいは、それは、ユーザーに、追加の入力データ検証を提供するために、妥当性検証規則を使用するかもしれません。
- ビューは、アニメーションや移行のような、UIの視覚的な動作を定義して、処理します。 それは、View Model内の状態変化から、あるいは、UIに対するユーザーの相互作用を通して、起動されるかもしれません。
- ビューの分離コードは、視覚的な動作を実装するために、UIロジックを定義するかもしれません。 それは、XAMLで表すのが困難です。あるいは、それは、ビューで定義される特定のUIのコントロールに直接参照する必要があります。
View Modelクラス
The View Model Class
MVVMパターン内のView Modelは、プレゼンテーション・ロジックとビューのためのデータをカプセル化します。 それは、ビューの特定の実装や型について、ビューやどんな知識にも直接の参照を持っていません。 View Modelは、プロパティとコマンドを実装します。その、ビューは、変更通知イベントを通して、データ結合とどんな状態の変更でもビューに通知できます。 View Modelで提供されるプロパティと命令は、UIで提供される、機能を定義します。 しかし、ビューは、その機能が、どのようにレンダリングかを決定します。
ビュー・モデルは、どんなモデル・クラスでも、ビューの相互作用を調整する役割があります。 それは、必要です。一般的に、ビュー・モデルとモデル・クラスの間に、1対多の関係があります。 View Modelは、モデル・クラス・ディレクトリをビューに公開することを選択するかもしれません。 それで、ビュー内のそのコントロールは、それらにディレクトリをデータ結合することができます。 この場合、クラス設計する必要があるモデルは、データ結合、そして、関連した変更通知イベントをサポートしています。 この筋書きの詳細については、この項目の後にある、データ結合の項目を参照してください。
View Modelは、変換する、あるいは、モデル・データを操作するかもしれません。 それで、それは、ビューで、簡単に利用することができます。 View Modelは、特にビューをサポートするために、追加されたプロパティを定義するかもしれません。; これらのプロパティは、通常の(あるいは、追加することができない)モデルの一部でありません。 例えば、View Modelは、ビューが簡単に存在するために、2つのフィールドの値を結合するかもしれません。 あるいは、それは、最大の長さのフィールドのために、入力の残りの文字の数を計算するかもしれません。 また、View Modelは、データの整合性を確保するために、データ確認ロジックを実装するかもしれません。
また、View Modelは、論理状態を定義するかもしれません。ビューは、UIで外観の変更を提供するために、使用することができます。 ビューは、レイアウトやスタイル変更を定義するかもしれません。 それは、View Modelの状態を反射します。例えば、View Modelは、指示される状態を定義するかもしれません。 そのデータは、Webサービスに非同期で送信されています。 ビューは、ユーザーに、視覚的なフィードバックを提供するこの状態の間、アニメーションを表示することができます。
一般的に、View Modelは、UIで表示されることができる、命令や動作を定義し、そして、ユーザーは、それを呼び出すことができます。 一般的な例は、View Modelが、Submitコマンドを提供するとき、それは、ユーザーが、Webサービス、あるいは、データリポジトリに、データを送信することができます。 ビューは、ボタンで、その命令を表示することを選択をするかもしれません。ユーザーが、データを送信するために、ボタンをクリックすることができるように、 一般的に、コマンドが利用できなくなるとき、その関連するUIの表示は無効にされます。 コマンドは、UIのそれらの外観の表示から、きれいにそれらを分離するために、ユーザーの動作をカプセル化する方法を提供します。
要約すると、View Modelには次の重要な特性があります:
- View Modelは、非ビジュアル・クラスです。そして、WPFのどんな基底クラスからも派生しません。 それは、アプリケーションで、使用事例、あるいは、ユーザー・タスクをサポートするために、 要求されるプレゼンテーション・ロジックをカプセル化します。 View Modelは、ビューとモデルと独立し、テストしやすいです。
- View Modelは、一般的に、直接、ビューを参照しません。ビューは、プロパティと命令を実装しデータ結合できます。 それは、変更通知イベントを通して、どんな状態の変更でも、 INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを通じて、ビューに通知します。
- View Modelは、ビューの相互作用をモデルと調整します。それが、ビューとモデルに存在しないかもしれない 、実装されるかもしれない追加されたプロパティによって、簡単に利用することができるように、データを変換する、あるいは、操作するかもしれません。 また、それは、IDataErrorInfoやINotifyDataErrorInfoインターフェイスを通して、データ検証を実装するかもしれません。
- View Modelは、ビューが、ユーザーに視覚的に表示することができる論理状態を、定義するかもしれません。
備考
ビュー、あるいは、View model?
何度も、特定の機能が発見される場所は、明らかにしないで実装する必要があります。 一般的な目安は以下の通りです:UIの画面上での特定の視覚的な外観に関係している何かは、 後で、スタイルを更新することができます。(たとえ、あなたが、現在、スタイルを更新する予定がないとしても)ビューの中に、入れる必要があります。; どんなものでも、アプリケーションの論理的な動作は、View Modelに行く必要があることは重要です。 加えて、なぜなら、View Modelは、ビュー内の特定の視覚的な要素の明確な知識を持っていてはいけません。 ビューの中で視覚的な要素をプログラム上で操作するコードは、ビューの分離コードに存在する、あるいは、動作にカプセル化する必要があります。 同様に、データ項目を取り出したり、操作するコードは、View Modelの中に存在する必要があるデータ結合を通じて、ビューの中で、表示されています。
例えば、リスト・ボックスで選択した項目の強調表示色は、ビューで定義する必要があります。 しかし、表示するための項目のリスト、そして、選択した項目それ自身への参照は、View Modelで定義する必要があります。
Modelクラス
The Model Class
MVVMパターン内のモデルは、ビジネス・ロジックとデータをカプセル化します。 ビジネス・ロジックは、どんなアプリケーション・ロジックでも、同じように定義されます。 それは、アプリケーションデータの検索と管理、そして、データの整合性と妥当性検証を確実に強要する、 どんなビジネス・ルールでも確実に作成することに関係します。 再使用の機会を最大にするために、モデルは、どんな使用事例も含むべきではありません。 -具体的な、あるいは、ユーザー・タスク-具体的な動作、あるいは、アプリケーション・ロジック。
一般的に、モデルは、アプリケーションのクライアント側のドメインモデルを表示します。 それは、アプリケーションのデータモデルに基づくデータ構造を定義し、そして、ビジネスと妥当性検証論理を何でもサポートします。 また、モデルは、データアクセスとキャッシュをサポートするための、コードが含まれているかもしれません。 しかし、一般的に、別々のデータリポジトリやサービスは、このために使用されます。 多くの場合、ADO.NET Entity Framework、WCFデータサービス、WCF RIAサービスのような、 モデルとデータアクセス層は、データアクセスやサービス戦略の一部として作成されます。
一般的に、モデルは、機能を実装します。ビューに結合することが容易になります。
これは、通常、それが、INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを通して、
プロパティと変更が通知されたコレクションをサポートすることを示しています。
一般的に、オブジェクトのコレクションを示すモデル・クラスは、ObservableCollection
一般的に、モデルは、機能を実装します。ビューに結合することが容易になります。
これは、通常、それが、INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを通して、
プロパティと変更が通知されたコレクションをサポートすることを示しています。
一般的に、オブジェクトのコレクションを示すモデル・クラスは、ObservableCollection
備考
あなたのモデル・クラスが、必要とするインターフェイスを実装しない場合、どうなるでしょうか?
時には、あなたは、INotifyPropertyChanged、INotifyCollectionChanged、IDataErrorInfoやINotifyDataErrorInfoインターフェースを実装していない、 モデル・オブジェクトで動作する必要があります。それらの場合において、View Modelは、 モデル・オブジェクトを包み、そして、必要なプロパティをビューに公開する必要があるかもしれません。 これらのプロパティのための値は、モデル・オブジェクトによって、直接、提供されるでしょう。 View Modelは、プロパティのために、必要とされるインターフェイスを実装します。 それは、ビューが、簡単に、それらにデータ結合することができるように公開します。
モデルは、次に示す、重要な特性を持っています。:
- Modelクラスは、アプリケーション・データとビジネス・ロジックをカプセル化する非ビジュアル・クラスです。 必要とされるビジネス・ルールとデータ検証ロジックをカプセル化することで、 それらは、アプリケーションのデータを管理するため、そして、その一貫性と妥当性を確実とするための役割を果たします。
- モデル・クラスは、ビューやView Modelクラスを、直接、参照しません。 そして、それらが、どのように実装されるかについての依存関係を持っていません。
- モデル・クラスは、一般的に、プロパティと変更通知コレクション・イベントを提供します。 INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを通して、これは、それらを簡単に、ビューにデータを結びつけることができます。 一般的に、オブジェクトのコレクションを示すモデル・クラスは、ObservableCollection<T>クラスから派生します。
- モデル・クラスは、IDataErrorInfoやINotifyDataErrorInfoインターフェイスのどちらかを通して、一般的に、データ検証とエラー報告を提供します。
- モデル・クラスは、一般的に、データアクセスとキャッシュをカプセル化する、サービスやリポジトリーと一緒に使用されます。
クラスの相互作用
Class Interactions
MVVMパターンは、あなたのアプリケーションのユーザー・インターフェイス、そのプレゼンテーション・ロジック、 そして、そのビジネス・ロジックとデータの間に、別々のクラスにそれぞれを分離することによって、完全な分離を提供します。 その結果、あなたが、MVVMを実装するとき、前の項目で説明されているように、 あなたのアプリケーションのコードを、適切なクラスに要素の1つとして含めることは、重要です。
うまく設計されたView、View ModelとModelクラスは、コードの適切な型と動作だけをカプセル化しないでしょう。; また、それらは、データ結合、コマンド、そして、データ検証インターフェイスを通して、 簡単に、互いに相互作用することができるように、設計されるでしょう。
ビューとそのビュー・モデルの間の相互作用は、おそらく、考慮することが、最も重要です。 しかし、また、モデル・クラスとビュー・モデルの間の相互作用は、重要です。 次の項目では、あなたのアプリケーションで、MVVMパターンを実装するとき、これらの相互作用のさまざまなパターンを説明し、 そして、それらを、どのように設計するか、説明します。
データ結合
Data Binding
データ結合は、MVVMパターンで極めて重要な役割を演じます。WPFは、強力なデータ結合機能を提供します。 あなたのView Modelと(理想的な)あなたのモデル・クラスは、これらの機能を利用することができるように、データ結合をサポートするように設計されている必要があります。 一般的に、これは、それらが、適切なインターフェースを実装する必要があることを示しています。
WPFのデータ結合は、複数のデータ結合モードをサポートしています。一方向のデータ結合では、UIコントロールは、View Modelに結合することができます。 そのため、表示が、レンダリングされるとき、それらは、基盤となるデータの値を反映します。 また、双方向のデータ結合は、ユーザーが、UIでそれを修正するとき、基盤となるデータを自動的に更新します。
View Modelで、データが、変更されるとき、適切な変更通知インターフェイスを実装する必要があるUIは、データを確実に維持します。 それが、データ結合するプロパティを定義する場合、それは、INotifyPropertyChangedインターフェイスを実装する必要があります。 View Modelが、コレクションを示す場合、INotifyCollectionChangedインターフェイスを実装する、 あるいは、このインターフェイスの実装を提供する、ObservableCollection<T>クラスから派生する必要があります。 これらのイベントを定義する両方のインターフェイスは、基盤となるデータが変わるたびに、いつでも、発生させます。 どんな、結合されたコントロールのデータも、これらのイベントが、発生する時、自動的に、更新されるでしょう。
多くの場合、View Modelは、オブジェクトを返すプロパティを定義します(そして、順番に、追加されたオブジェクトを返す、プロパティを定義するかもしれません)。 WPFのデータ結合は、Pathプロパティを通して、入れ子にされたプロパティへの結合をサポートします。 その結果、ビューのView Modelが、他のView ModelやModelクラスに参照を返すことは、極めて一般的です。 ビューにアクセスできる、すべてのビュー・モデルとモデル・クラスは、適切なように、INotifyPropertyChangedや INotifyCollectionChangedインターフェイスを実装している必要があります。
次のセクションでは、MVVMパターン内でデータ結合をサポートするために、必要とされるインターフェイスを、どのように実装するかを説明します。
INotifyPropertyChangedを実装する
Implementing INotifyPropertyChanged
あなたのView Modelで、INotifyPropertyChangedインターフェイスを実装する、あるいは、モデル・クラスは、 基盤となるプロパティの値が変わるとき、それらに、ビュー内のどんなデータ結合されたコントロールにでも変更通知を提供できます。 次のコードの例に示すように、このインタフェースを実装すると、簡単です。
public class Questionnaire : INotifyPropertyChanged
{
private string favoriteColor;
public event PropertyChangedEventHandler PropertyChanged;
...
public string FavoriteColor
{
get { return this.favoriteColor; }
set
{
if (value != this.favoriteColor)
{
this.favoriteColor = value;
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this,
new PropertyChangedEventArgs("FavoriteColor"));
}
}
}
}
}
多くのView Modelクラスで、INotifyPropertyChangedインターフェイスを実装することは、 イベントの引数に、プロパティ名を指定する必要があるため、反復的なので、エラーが発生しやすくなります。Prismライブラリは、ここに、示されるように、 あなたが、タイプ・セーフな方法で、INotifyPropertyChangedインターフェイスを実装する、 View Modelクラスを派生できる、BindableBase基底クラスを提供しています。:
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
...
protected virtual bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] string propertyName = null)
{...}
protected void OnPropertyChanged<T>(
Expression<Func<T>> propertyExpression)
{...}
protected void OnPropertyChanged(string propertyName)
{...}
}
派生したView Modelクラスは、SetPropertyメソッドを呼び出すことによって、セッターで、プロパティ変更イベントを発生させることができます。 SetPropertyメソッドは、既に、設定されている値から、支援領域が異なるかどうか調べます。 異なる場合、支援領域は、更新され、そして、PropertyChangedイベントが呼び出されます。
次のコードの例は、OnPropertyChangedメソッドのラムダ式を使用して、プロパティを設定し、同時に、他のプロパティの変更を、 どのように通知するかを示しています。この例は、株トレーダーRIを参考にしています。TransactionInfoとTickerSymbolプロパティは、関連があります。 TransactionInfoプロパティを変更する場合、また、TickerSymbolは、おそらく、TickerSymbolプロパティのOnPropertyChangedを呼び出すことによって、 TransactionInfoプロパティのセッターで、更新されるでしょう。2つのPropertyChangedイベントは、 TransactionInfoのために1つ、TickerSymbolのために1つ、呼び出されるでしょう。
public TransactionInfo TransactionInfo
{
get { return this.transactionInfo; }
set
{
SetProperty(ref this.transactionInfo, value);
this.OnPropertyChanged(() => this.TickerSymbol);
}
}
備考
このように、ラムダ式を使用することは、小さな処理コストが含まれています。 なぜなら、ラムダ式は、それぞれの呼び出しのために、評価する必要があります。 利点は、この方法は、コンパイルの際、型の安全性を提供することです。 そして、あなたが、プロパティ名を変更する場合、リファクタリングは、サポートしています。 処理コストは小さく、通常、あなたのアプリケーションに影響を与えませんが、あなたが、多くの変更通知を持つ場合、コストが生じます。 この場合、あなたは、非ラムダ・メソッドの上書きの使用を考える必要があります。
多くの場合、あなたのModelやView Modelは、ModelやView Model内の他のプロパティから値が計算される、プロパティが含まれているでしょう。 また、プロパティの変更を扱う際には、どんな計算されたプロパティのためでも、通知イベントを必ず発生させます。
INotifyCollectionChangedを実現する
Implementing INotifyCollectionChanged
あなたのView ModelやModelクラスは、項目のコレクションを表すかもしれません。 あるいは、それは、項目のコレクションを返す、1つ以上のプロパティを定義するかもしれません。 いずれにせよ、あなたが、ItemsControlで、コレクションを表示したくなるかもしれません。 ListBoxやビュー内のDataGridコントロールのような、これらのコントロールは、コレクション示す、 あるいは、ItemSourceプロパティ経由で、コレクションを返す、プロパティをView Modelに結合することができます。
<DataGrid ItemsSource="{Binding Path=LineItems}" />
それが、コレクションを表し、INotifyCollectionChangedインターフェイスを実装する場合、 適切にView ModelやModelクラスが要求する変更通知をサポートするために、(INotifyPropertyChangedインターフェイスに加えて)。 View ModelやModelクラスがプロパティを定義する場合、それは、コレクションに参照を返します。 返されるコレクション・クラスは、INotifyCollectionChangedインターフェイスを実装する必要があります。
しかしながら、INotifyCollectionChangedインターフェイスを実装することが、困難な場合があります。 なぜなら、それは、コレクション内の項目が、追加される、削除される、変更されるとき、通知を提供する必要があります。 それは、多くの場合、インターフェイスを実装しているディレクトリの代わりに、使用する、 あるいは、既に、それを実装している、コレクション・クラスから派生することが、さらに、簡単です。 ObservableCollection<T>クラスは、このインターフェイスの実装を提供します。 そして、一般的に、基底クラスか項目のコレクションを表す実装プロパティのどちらかとして、使用されます。
あなたが、データ結合のためのビューに、コレクションを提供する必要がある場合、
そして、あなたは、ユーザーの選択を追跡する、あるいは、フィルタリング、並べ替えやコレクション内の項目のグループ化をサポートする必要は、ありません。
あなたは、単純に、ObservableCollection
public class OrderViewModel : BindableBase
{
public OrderViewModel( IOrderService orderService )
{
this.LineItems = new ObservableCollection(
orderService.GetLineItemList() );
}
public ObservableCollection LineItems { get; private set; }
}
あなたが、コレクション・クラスの参照を取得する場合、(例えば、他のコンポーネントやINotifyCollectionChangedを実装しないサービスから)、 あなたは、多くの場合、コンストラクタの1つを使用して、ObservableCollection<T>インスタンスで、そのコレクションを包むことができます。 IEnumerable<T>やList<T>パラメータを取得します。
備考
BindableBaseは、Microsoft.Practices.Prism.Mvvm名前空間で見つかります。 それは、Prism.Mvvm NuGetパッケージで配置されます。
ICollectionViewを実装する
Implementing ICollectionView
前述のコードの例は、ビューで、コントロールに結合されたデータを通して表示することができる項目のコレクションを返す、 単純なView Modelプロパティを、どのように実行するかを示します。ObservableCollection<T>クラスが、 INotifyCollectionChangedインターフェイスを実装するため、ビュー内のコントロールは、 自動的に、項目が追加、あるいは、削除されているか、コレクション内の項目の現在のリストを反映して、更新されます。
しかしながら、あなたは、多くの場合、ビュー内で表示される項目のコレクションを、さらに詳細に制御することが必要だと思います。 あるいは、その中のView Modelそれ自身で、表示された項目のコレクションで、ユーザーの相互作用を追跡します。 例えば、あなたは、View Model内で、プレゼンテーション・ロジックで実装された、 フィルターリングや並べ替えられている項目のコレクションを提供する必要があるかもしれません。 あるいは、あなたは、ビュー内で、現在選択された項目を追跡する必要があるかもしれません。 そのため、View Modelで実装されるコマンドは、現在選択された項目に基づいて行動することができます。
WPFは、ICollectionViewインターフェイスを実装する、さまざまなクラスを提供することによって、 これらの筋書きをサポートします。このインターフェイスは、フィルターをかける、並べ替える、 あるいは、グループ化するコレクションを提供するために、プロパティとメソッドを提供します。 そして、追跡や変更するために、現在選択された項目を提供します。 WPFは、ListCollectionViewクラスを使用して、このインターフェイスの実装を提供します。
コレクションのViewクラスは、項目の基盤となるコレクションを包むことによって動作します。 そのため、それらは、自動に選択するトラッキングと並べ替え、フィルタリングとページングを提供することができます。 これらのクラスのインスタンスは、CollectionViewSourceクラスを使用しているXAML内で、 プログラム上で、または、宣言的に作成することができます。
備考
WPFでは、既定のコレクション・ビューは、 実際に、コントロールがコレクションに結合されるたびに、自動的に作成されます。
コレクション・ビュー・クラスは、関係の明確な分離を維持している間、ビュー内のUIとモデル内の基盤となるデータの間で、 基盤となるコレクションのための重要な状態情報を追跡するために、View Modelで使用することができます。 実質的に、CollectionViewsは、View Modelです。それは、特に、コレクションをサポートするように設計されています。
その結果、あなたが、コレクション内の、あなたのView Modelで、項目のフィルタリング、並べ替え、 グループ化や選択トラッキングを実装する必要がある場合、あなたのView Modelは、ビューに公開されている、 それぞれのコレクションのために、コレクション・ビュー・クラスのインスタンスを作成する必要があります。 あなたは、続いて、あなたのView Model内から、コレクション・ビュー・クラスによって、 CurrentChangedイベント、あるいは、コントロールのフィルタリング、並べ替え、 提供されるメソッドを使用するグループ化のように、選択変更イベントに登録することができます。
View Modelは、ICollectionViewの参照を返す、読取専用プロパティを実装する必要があります。 そのため、ビューのコントロールは、コレクション・ビュー・オブジェクトにデータ結合、そして、相互作用することができます。 ItemsControl基底クラスから派生する、 すべてのWPFコントロールは、自動的に、ICollectionViewクラスと相互作用することができます。
次のコードの例は、WPFで、現在選択した利用者を追跡するために、ListCollectionViewの使用を示しています。
public class MyViewModel : BindableBase
{
public ICollectionView Customers { get; private set; }
public MyViewModel( ObservableCollection<Customer> customers )
{
// Initialize the CollectionView for the underlying model
// and track the current selection.
// 基盤となるモデルのためのCollectionViewと現在の選択内容の追跡を初期化する。
Customers = new ListCollectionView( customers );
Customers.CurrentChanged +=SelectedItemChanged;
}
private void SelectedItemChanged( object sender, EventArgs e )
{
Customer current = Customers.CurrentItem as Customer;
...
}
...
}
ここに、示されるように、ビューで、あなたは、続いて、ListBoxのような、そのItemsSourceプロパティを通じて、 View ModelのCustomersプロパティに、ItemsControlを結合することができます。:
<ListBox ItemsSource="{Binding Path=Customers}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ユーザーが、UIで利用者を選択するとき、View Modelは、現在選択された利用者に関係するコマンドが、 適用することができることを知らされます。 次のコードの例に示すように、また、View Modelは、UIで、コレクション・ビュー・オブジェクトでメソッドを呼び出すことによって、 現在の選択した内容をプログラム上で変更することができます。
Customers.MoveCurrentToNext();
選択がコレクション・ビューで変更されると、UIは、自動的に、見えるように表示するために、項目の選択された状態を更新します。
コマンド
Commands
ビューで表示する、あるいは、編集するデータにアクセスを提供することに加えて、View Modelは、おそらく、ユーザーによって実行することができる、 1つ以上の動作や操作を定義します。WPFでは、ユーザーが、UIによって実行することができる動作や操作は、一般的に、コマンドとして定義されています。 コマンドは、動作や操作を表示するために、簡単に、UIで、コントロールに結合することができる、便利な方法を提供します。 それらは、動作や操作を実装する、実際のコードをカプセル化します。 そして、それを、ビュー内で、その実際の外観の表示から、分離し続けるのを助けます。
コマンドは、それらがビューと対話するように、ユーザーによって多くの異なる方法で、視覚的に表示する、そして、呼び出すことができます。 ほとんどの場合、それらは、マウスクリックの結果として呼び出されます。 しかし、また、それらは、ショートカット・キー押す、タッチ・ジェスチャー、あるいは、他の入力イベントの結果として呼び出すことができます。 ViewのControlは、View Modelのコマンドに結合されるデータです。 そのため、ユーザーは、どんな入力イベントやコントロールで定義するジェスチャーでも使用して、それらを呼び出すことができます。 ビューのUIコントロールの間の相互作用とコマンドは、双方向にすることができます。 この場合、コマンドは、ユーザーがUIと対話するように、呼び出すことができます。 そして、UIは、基盤となるコマンドが、有効や無効になるように、自動的に、有効や無効にすることができます。
View Modelは、どちらのCommandメソッドとして、あるいは、Commandオブジェクトとしてでも、コマンドを実装することができます。 (オブジェクトは、ICommandインターフェイスを実装する)。 いずれにせよ、コマンドに対するビューの相互作用は、宣言的に定義することができます。 ビューのコード・ビハインド・ファイルで、複雑なイベント処理コードを必要とすることなく、 例えば、WPFの特定のコントロールは、本質的に、コマンドをサポートして、View Modelで、 提供されるICommandオブジェクトにデータ結合することができるCommandプロパティを提供します。 他の場合には、コマンドの動作は、View Modelによって提供される、 コマンドのメソッドやコマンドのオブジェクトを、コントロールに関連付けるために使用することができます。
備考
ビヘイビアは、相互作用ロジックと動作をカプセル化するために使用することができる、 強力で柔軟な拡張の仕組みです。それは、続いて、ビューでコントロールに宣言的に関連付けることができます。 コマンドの動作は、コマンドと相互作用するように特に設計されていなかったコントロールで、 コマンド・オブジェクトやメソッドを、関連付けるために使用することができます。
次のセクションは、あなたのビューで、コマンドを、コマンド・メソッドとして、 または、コマンド・オブジェクトとして、どのように実装するか、そして、ビューでコントロールを、どのように関連付けるかを説明します。
作業に基づいたデリゲート・コマンドを実装する
Implementing a Task-Based Delegate Command
コマンドは、UIスレッドをブロックすることができない、実行時間の長いトランザクションで、 コードを呼び出す多くの筋書きがあります。これらの筋書きのために、 あなたは、非同期ハンドラ・メソッドから、 DelegateCommandの新しいインスタンスを作成する、DelegateCommandクラスの、 FromAsyncHandlerメソッドを使用する必要があります。
// DelegateCommand.cs
public static DelegateCommand FromAsyncHandler(Func<Task> executeMethod, Func<bool> canExecuteMethod)
{
return new DelegateCommand(executeMethod, canExecuteMethod);
}
例えば、次のコードは、デリゲートを、SignInAsyncとCanSignInのビュー・モデルのメソッドに、 指定することによって構築される、コマンドの記号を表す、DelegateCommandが、どのようにインスタンスを作成するかを示します。 コマンドは、続いて、ICommandに参照を返す、読取専用プロパティを通してビューに公開されます。
// SignInFlyoutViewModel.cs
public DelegateCommand SignInCommand { get; private set; }
...
SignInCommand = DelegateCommand.FromAsyncHandler(SignInAsync, CanSignIn);
コマンド・オブジェクトを実装する
Implementing Command Objects
コマンド・オブジェクトは、ICommandインターフェイスを実装するオブジェクトです。 このインターフェイスは、操作それ自身とコマンドが、特定の時間に呼び出されることができるかどうかを示す、 CanExecuteメソッドをカプセル化する、Executeメソッドを定義します。 これらのメソッドの両方は、コマンドのためのパラメータとして、一つの引数を取得します。 コマンド・オブジェクトの操作の実装ロジックのカプセル化は、さらに簡単に、ユニットをテストし、保守することができることを示します。
ICommandインターフェイスを実装するのは、簡単です。 しかしながら、あなたが、すぐに、アプリケーションの中で使用するための、このインターフェイスのいくつかの実装があります。 例えば、あなたは、Visual Studio SDKのために、Blendから、 ActionCommandクラスを使用することができます。 あるいは、DelegateCommandクラスは、Prismによって、提供されます。
備考
DelegateCommandは、Prism.Mvvm NuGetパッケージで配置される、Microsoft.Practices.Prism.Mvvm名前空間で見つかります。
PrismのDelegateCommandクラスは、メソッドのそれぞれの参照は、あなたのView Modelクラスの範囲内で、それを実装した、 2つのデリゲートをカプセル化します。それは、これらのデリゲートを呼び出すことによって、 ICommandインターフェイスのExecuteとCanExecuteメソッドを実装する、DelegateCommandBaseクラスから継承します。 あなたは、次のように定義されている、DelegateCommandクラス・コンストラクタ内の、View Modelメソッドにデリゲートを指定します。
// DelegateCommand.cs
public class DelegateCommand<T> : DelegateCommandBase
{
public DelegateCommand(Action<T> executeMethod,Func<T,bool> canExecuteMethod ): base((o) => executeMethod((T)o), (o) => canExecuteMethod((T)o))
{
...
}
}
例えば、次のコードの例は、Submitコマンドを示すDelegateCommandインスタンスを、どのように、作成するかを示します。 デリゲートを、OnSubmitとCanSubmitのView Modelメソッドに指定することによって構築されます。 コマンドは、続いて、ICommandに参照を返す、読取専用プロパティを通してビューに公開されます。
public class QuestionnaireViewModel
{
public QuestionnaireViewModel()
{
this.SubmitCommand = new DelegateCommand<object>(
this.OnSubmit, this.CanSubmit );
}
public ICommand SubmitCommand { get; private set; }
private void OnSubmit(object arg) {...}
private bool CanSubmit(object arg) { return true; }
}
Executeメソッドが、DelegateCommandオブジェクトに呼び出されるとき、それは、単純に、あなたが、コンストラクタ内で指定するデリゲートを通して、 あなたのView Modelクラス内のメソッドに呼び出しを転送します。 同様に、CanExecuteメソッドが、呼び出されるとき、あなたのView Modelクラスの対応するメソッドは、呼び出されます。 コンストラクタ内のCanExecuteメソッドへのデリゲートは、選択可能です。 デリゲートが、指定されない場合、DelegateCommandは、常に、CanExecuteのためにtrueを返します。
DelegateCommandクラスは、ジェネリック型です。 型引数は、ExecuteとCanExecuteメソッドに渡されるコマンド・パラメータの型を指定します。前述の例では、コマンド・パラメータは、型オブジェクトです。 コマンド・パラメータが、必要とされていないとき、また、DelegateCommandクラスの非ジェネリックのバージョンは、使用するために、Prismによって提供されます。
View Modelは、DelegateCommandオブジェクトで、RaiseCanExecuteChangedメソッドを呼び出すことによって、 コマンドのCanExecuteステータスで変化を示すことができます。 これは、CanExecuteChangedイベントの発生をもたらします。コマンドに結合した、 UIのどんなコントロールでも、結合されたコマンドの利用を反映するために、それらの有効ステータスを更新するでしょう。
ICommandインターフェイスの他の実装は、利用できます。Expression Blend SDKで提供されるActionCommandクラスは、 先に説明した、PrismのDelegateCommandクラスに似ています。しかし、それは、一つのExecuteメソッド・デリゲートだけをサポートします。 また、Prismは、CompositeCommandクラスを提供します。それは、DelegateCommandsが、実行するために互いにグループ化できます。 CompositeCommandクラスを使用することに関する詳細は、 「高度なMVVMの筋書き」(原文リンク) で「複数の要素で構成されたコマンド」(原文リンク)を参照してください。
ビューからコマンド・オブジェクトを呼び出す
Invoking Command Objects from the View
ビュー内のコントロールのいくつかの方法は、View Modelで提供されるコマンド・オブジェクトに関連付けることができます。 ButtonやRadioButton、そして、HyperlinkやMenuItemから派生したコントロールのような、 特定のWPFコントロール、特にButtonBaseから派生したコントロール、簡単に、Commandプロパティを通して、コマンド・オブジェクトにデータ結合できます。 また、WPFは、View Model ICommandをKeyGestureに結合することをサポートします。
<Button Command="{Binding Path=SubmitCommand}" CommandParameter="SubmitOrder"/>
また、コマンド・パラメータは、必要に応じて、CommandParameterプロパティを使用して、定義することができます。 期待される引数の型は、ExecuteとCanExecuteの対象メソッドを指定します。 コントロールは、ユーザーがそのコントロールとコマンド・パラメータで相互作用するとき、対象とするコマンドを自動的に呼び出します。 提供される場合、コマンドのExecuteメソッドへの引数に渡されるでしょう。 前述の例では、ボタンは、自動的に、それがクリックされるSubmitCommandを呼び出します。 さらに、CanExecuteが、falseを返すと、CanExecuteハンドラが、指定される場合、ボタンは、自動的に無効になります。 そして、それが、trueを返す場合、それは、有効になるでしょう。
Visual Studio 2013のBlendを使用するための代わりの方法は、 相互作用を起動する、そして、InvokeCommandAction動作です。 イベントのInvokeCommandAction動作と関連するコマンドの詳細については、 「高度なMVVMの筋書き」(原文リンク)の中の「トリガとコマンドの相互作用」(原文リンク)参照してください。
データ検証とエラー報告
Data Validation and Error Reporting
あなたのView modelやModelは、多くの場合、データ検証を実行し、 ビューにどんなデータ検証エラーでも通知することを要求します。 そのため、ユーザーは、それらを修正するために作用させることができます。
WPFは、ビューのコントロールに結合する、それぞれのプロパティを変更するとき、 発生するデータ検証エラーを管理するためのサポートを提供します。 コントロールにデータ結合するための、一つのプロパティは、View modelやModelは、 プロパティ・セッターの範囲内でデータ検証エラーを知らせることができます。 入力される無効な値を拒否して、例外を投げることによって、データ結合のValidatesOnExceptionsプロパティが、 trueの場合、WPFのデータ結合エンジンは、例外を処理し、 そして、データ検証エラーがあることをユーザーに、視覚的な合図を表示します。
しかしながら、このように、プロパティで例外を投げることは、可能な限り、避ける必要があります。 代わりの方法は、あなたのView ModelやModelクラスで、IDataErrorInfoやINotifyDataErrorInfoインターフェイスを実装することです。 これらのインターフェイスは、データ検証を実行するために、 1つ以上のプロパティ値のために、そして、ビューにエラーメッセージを返すために、 ユーザーが、エラーを通知することができるように、あなたのView modelやModelを提供します。
IDataErrorInfoを実装する
Implementing IDataErrorInfo
IDataErrorInfoインターフェイスは、プロパティのデータ検証のための基本的なサポートとエラー報告を提供します。 それは、2つの読取専用プロパティを定義します。:indexerプロパティ、 indexer引数としてプロパティ名、そして、Errorプロパティ。両方のプロパティは、文字列の値を返します。
indexerプロパティは、名前を付けたプロパティに具体的なエラーメッセージを提供するために、View ModelやModelクラスを提供します。 空の文字列やnull戻り値は、変更されたプロパティの値が有効であることをビューに示します。Errorプロパティは、View ModelやModelクラスに、 全オブジェクトのためのエラーメッセージを提供することができます。 しかしながら、このプロパティは、WPFのデータ結合エンジンで、現在、それから、呼び出されないことに注意してください。
データ結合されたプロパティが、最初に表示されるとき、 そして、それが、その後、変更されるたびに、IDataErrorInfo indexerプロパティがアクセスされます。 indexerプロパティが、変更されるすべてのプロパティのために呼び出されるため、 あなたは、データ検証が、可能な限り速く、効率的であることを注意深く確認する必要があります。
あなたが、ビューのコントロールをプロパティに結合するとき、あなたは、データ結合のValidatesOnDataErrorsプロパティを、 trueに設定する、IDataErrorInfoインターフェイスを通して、検証することを望みます。 これは、データ結合エンジンが、データ結合されたプロパティのための、エラー情報を要求することを確認するでしょう。
<TextBox
Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True }"
/>
INotifyDataErrorInfoを実装する
Implementing INotifyDataErrorInfo
INotifyDataErrorInfoインターフェイスは、IDataErrorInfoインターフェイスに比べてより柔軟です。 それは、プロパティの複数のエラー、非同期のデータ検証、そして、エラー状態が、オブジェクトのために変更される場合、 ビューに通知する機能をサポートしています。
INotifyDataErrorInfoインターフェイスは、View Modelが、プロパティに存在するエラー(または複数のエラー)、 そして、View Modelが、特定のプロパティのために、エラーメッセージのリストを返すことができる、 GetErrorsメソッドのどちらかを示すことができる、HasErrorsプロパティを定義します。
また、INotifyDataErrorInfoインターフェイスは、ErrorsChangedイベントを定義します。 これは、ErrorsChangedイベントを通して、特定のプロパティのためのエラー状態の変更を知らせるために、 ViewやView Modelを提供することによって、非同期の妥当性検証の筋書きをサポートします。 プロパティ値は、データ結合を通してではない、いくつかの方法で変更することができます。 -例えば、webサービスコールやバックグラウンド計算の結果として、ErrorsChangedイベントは、 一旦、データ検証エラーが識別される場合、View Modelは、ビューにエラーを知らせることができます。
INotifyDataErrorInfoに対応するために、あなたは、それぞれのプロパティのためのエラー・リストを維持する必要があるでしょう。 Model-View-ViewModelの参考になる実装(MVVM RI)は、オブジェクト内の、すべての妥当性検証エラーを追跡する、 ErrorsContainerコレクション・クラスを使用して行うために、片方向を説明します。 また、それは、エラー・リストが変更される場合、通知イベントを発生させます。 次に示すコードの例は、DomainObject(ルート・モデル・オブジェクト)を示し、 そして、ErrorsContainerクラスを使用する、INotifyDataErrorInfoの実装の例を示します。
public abstract class DomainObject : INotifyPropertyChanged,
INotifyDataErrorInfo
{
private ErrorsContainer<ValidationResult> errorsContainer =
new ErrorsContainer<ValidationResult>(
pn => this.RaiseErrorsChanged( pn ) );
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors
{
get { return this.ErrorsContainer.HasErrors; }
}
public IEnumerable GetErrors( string propertyName )
{
return this.errorsContainer.GetErrors( propertyName );
}
protected void RaiseErrorsChanged( string propertyName )
{
var handler = this.ErrorsChanged;
if (handler != null)
{
handler(this, new DataErrorsChangedEventArgs(propertyName) );
}
}
...
}
構築と接続
Construction and Wire-Up
MVVMパターンは、あなたが、あなたのUIをあなたのプレゼンテーションとビジネス・ロジックとデータから、きれいに分離するに役立ちます。 それで、正しいクラスで正しいコードを実装することは、効果的にMVVMパターンを使用する際の重要な最初の手順です。 また、データ結合とコマンドを通して、ViewとView Modelクラスの間の相互作用を管理することは、考えるための重要な測面です。 次の手順は、View、View ModelとModelクラスが、実行時に、互いに、どのようにインスタンスを生成し、関連づけるか、考えます。
備考
この手順を管理するために、適切な戦略を選択ことは、特に重要です。 あなたが、あなたのアプリケーションの依存関係注入コンテナを使用している場合、 拡張管理フレームワーク(MEF)とUnityアプリケーション・ブロック(Unity)の両方は、 View、View ModelとModelクラスと間で、それらを持つために、コンテナで、実現する、指定する依存関係の機能を提供します。 より高度な筋書きのために、高度なMVVMの筋書き(原文リンク)を参照してください。
一般的に、ViewとそのView Modelの間に、1対1のリレーションシップがあります。ViewとView Modelは、ビューのデータ・コンテクスト・プロパティを通して、 疎く結合しています。;これは、ビュー内で、View Modelのプロパティ、コマンドとメソッドに結合されるデータの中で、視覚的な要素と動作を提供します。 あなたは、実行時に、DataContextプロパティを通して、ViewとView Modelクラスとそれらの関連付けのインスタンス化を、どのように管理するか、決定する必要があるでしょう。
また、注意する必要があります。疎結合が保持されているか確認するために、ViewとView Modelを構築し、結合するとき、先の項目で指摘したように、 View Modelは、理想的には、どんな、ビューの具体的な実装にも依存してはいけません。 同様に、ビューは、理想的には、どんな、View Modelの具体的な実装にも依存してはいけません。
備考
しかしながら、それは、Viewが、View Modelの具体的なプロパティ、コマンドとメソッドに暗黙的に依存すると思われることに、 注意する必要があります。それが、定義するデータ結合のため、View Modelが、必要とされるプロパティ、コマンドやメソッドを実装しない場合、 実行時例外は、デバッグの間、Visual Studio出力ウィンドウで表示される、データ結合エンジンで作成されます。
ViewとView Modelが、実行時に、構築し、関連付けることができる複数の方法が、あります。 あなたのアプリケーションのための最も適切な方法は、主に、あなたが、最初に、ViewやView Modelを作成するかどうか、 そして、あなたが、プログラム上で、あるいは、宣言的に、これを行うかどうか、次第です。次のセクションでは、一般的な方法を説明します。 ViewとView Modelクラスは、実行時に、それに、互いに、作成し、関連付けることができます。
XAMLを使用して、View modelを作成する
Creating the View Model Using XAML
おそらく、最も単純な方法は、ビューに宣言的に、XAMLで、View Modelの、その対応するインスタンスを生成することです。ビューが構築されるとき、 対応するView Modelオブジェクトもまた、構築されます。 また、あなたは、View Modelが、ビューのデータ・コンテクストとして設定されているXAMLで指定することができます。
<UserControl.DataContext>
<my:MyViewModel/>
</UserControl.DataContext>
このビューが作成されるとき、MyViewModelのインスタンスは、ビューのデータ・コンテクストとして自動的に構築されて、設定されます。 この方法は、既定の(パラメータのない)コンストラクタを持つために、あなたのView Modelを必要とします。
ビューで、View Modelの宣言型の構築と割当てを行うことは、Microsoft Expression Blendや Microsoft Visual Studioのような、設計時のツールで、それを単純に、そして、うまく動作するために、利点があります。 この方法の欠点は、ビューが、対応するView Model型についての知識があるということです。そして、 View Model型は、既定のコンストラクタを持つ必要があります。
プログラム上でView modelを作成する
Creating the View Model Programmatically
他の方法は、ビューのために、プログラム上の、そのコンストラクタ内で、それをその対応するView Modelインスタンスに、インスタンス化することです。 次のコードの例に示すように、それは、続いて、そのデータ・コンテクストとして、それを設定することができます。
public MyView()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
View Modelのプログラムに基づいた構築と割当ては、ビューの分離コードの範囲内で、利点があります。 Expression BlendやVisual Studioのように、設計時にツール内で、それが簡単で、うまく動作します。 この方法の欠点は、ビューが対応するView Model型についての知識が必要だということです。 そして、それは、ビューの分離コード内で、コードを必要とします。UnityやMEFのような、 依存関係注入コンテナを使用することは、ViewとView Modelの間で、疎結合を維持することに役立ちます。 詳細については、コンポーネントの間で、依存関係を管理する(原文リンク)を参照してください。
View modelロケーターを使用して、View modelを作成する
Creating the View Model Using a View Model Locator
View Modelロケーターを使用して、View Modelインスタンスを作成する他の方法とそのビューによるそれがある同僚をします。
ViewModelLocationProviderクラス内で、ビューのためのView Modelを解決するために、AutoWireViewModelChangedメソッドの呼び出しを設定するとき、 PrismのView Modelロケーターは、AutoWireViewModel添付プロパティを持っています。デフォルトで、それは、規則に基づく方法を使用します。
基本的なMVVMクイックスタートでは、MainWindow.xamlは、View Modelを解決するView Modelロケーターを使用します。
...
prism:ViewModelLocator.AutoWireViewModel="True">
添付プロパティを持っている、PrismのViewModelLocatorクラスは、trueに設定する時、 AutoWireViewModelは、ビューのView Modelを配置しようと試すでしょう。 そして、その次に、Viewのデータ・コンテクストを、View Modelのインスタンスに設定します。 対応するView Modelを配置するために、ViewModelLocationProviderは、まず、どんなマッピングからでも、 View Modelを解決ために、試みます。それは、ViewModelLocationProviderクラスのRegisterメソッドによって登録されたかもしれません。 View Modelが、この方法を使用して、解決することができない場合、たとえば、マッピングが作成されない場合、ViewModelLocationProviderは、 正しいView Model型を解決するために、規則に基づいた方法に戻します。この規則は、View Modelが、ビュー型として、同じアセンブリ内にある事を前提とします。 そのView Modelは、.ViewModels child名前空間にあります。そのビューは、.Views child名前空間にあります。 そして、View Model名は、View名と「ViewModel.」の末端に一致します。 PrismのView Model Locator規則を、どのように変更するかの説明のために、 付録E:Prismを拡張する(原文リンク)を参照してください。
備考
Prism.Mvvm NuGetパッケージで配置される、ViewModelLocationProviderはMicrosoft.Practices.Prism.Mvvmアセンブリで見つかり、 そして、ViewModelLocatorはMicrosoft.Practices.Prism.Mvvm.Desktopアセンブリで見つかります。
データ・テンプレートとして定義されるビューを作成する
Creating a View Defined as a Data Template
ビューは、データ・テンプレートとして、定義され、View Model型で関連付けることができます。 データ・テンプレートは、リソースとして、定義することができます。 あるいは、それらは、View Modelを表示する、コントロールの内のインラインで定義することができます。 コントロールの「内容」は、View Modelインスタンスです。 そして、データ・テンプレートは、視覚的にそれを表示するために使用されます。 WPFは、自動的に、データ・テンプレートのインスタンスを生成します。 そして、そのデータ・コンテクストを、実行時のView Modelインスタンスに設定します。 この技術は、状況の例です。 View Modelが、まず、その中で、インスタンス化され、続いて、ビューが作成されます。
データ・テンプレートは、柔軟で、軽量です。UIデザイナーは、どんな複雑なコードも必要とすることなく、 簡単に、View Modelの視覚的な表現を定義するために、それらを使用することができます。 データ・テンプレートは、どんなUIロジック(分離コード)も必要としない、ビューに制限されます。 視覚的にデータ・テンプレートを設計して、編集するために、 Visual Studio 2013のためのMicrosoft Blendは、使用することができます。
次に示す例は、顧客のリストに結合されるItemsControlを示しています。 基盤となるコレクションの中のそれぞれの利用者のオブジェクトは、View Modelインスタンスです。 顧客のためのビューは、インライン・データ・テンプレートによって定義されています。 次の例では、それぞれの顧客のView Modelのためのビューは、 StackPanelのラベルとView ModelでNameプロパティに結合されるテキストボックス・コントロールから構成されています。
<ItemsControl ItemsSource="{Binding Customers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="Customer Name: " />
<TextBox Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
また、あなたは、リソースとして、データ・テンプレートを定義することができます。 次の例は、データ・テンプレートが、リソースを定義し、 StaticResourceマークアップ拡張機能によってコンテンツ・コントロールに適用することを示しています。
<UserControl ...>
<UserControl.Resources>
<DataTemplate x:Key="CustomerViewTemplate">
<local:CustomerContactView />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl Content="{Binding Customer}"
ContentTemplate="{StaticResource CustomerViewTemplate}" />
</Grid>
</UserControl>
ここで、データ・テンプレートは、具体的なビュー型をラップします。これは、ビューが、分離コードの動作を定義できます。 このように、データ・テンプレートの仕組みは、ViewとView Modelの間で、関連付けを外部的に提供するために使用することができます。 前述の例は、UserControlリソースでテンプレートを示しますが、それは、多くの場合、再使用のために、アプリケーション・リソースに配置されます。
重要な決定事項
Key Decisions
あなたが、あなたのアプリケーションを構築するために、MVVMパターンを使用することを選択するとき、 あなたは、後で変更するのが難しい、特定の設計決定を作成する必要があります。 通常、これらの決定は、アプリケーションで広範囲に、 そして、それらは、アプリケーションを通じて、一貫して使用され、開発者と設計者の生産性を改善するでしょう。 次の要約は、MVVMパターンを実装するとき、最も重要な決定をまとめています。:
- あなたが使用するViewとView Modelを構築する方法を決定します。あなたは、あなたのアプリケーションが、 まず、ViewやView Modelを構築する場合、そして、UnityやMEFのような、依存関係注入コンテナを使用するかどうかにかかわらず、決定する必要があります。 あなたは、通常、これが、アプリケーションで広範囲に一貫性のあることを望みます。 詳細については、構築と接続の項目と高度な構築と接続、高度なMVVMの筋書きの項目を参照してください。
- あなたが、コマンド・メソッドやコマンド・オブジェクトとして、あなたのView Modelから、コマンドを公開する場合、決定します。 コマンド・メソッドは、簡単に公開でき、ビュー内で動作を通して呼び出すことができます。 コマンド・オブジェクトは、コマンドをきちんとカプセル化することができます。そして、有効/無効ロジック、 そして、動作を通じて、あるいは、ButtonBaseから派生したコントロールのCommandプロパティを通じて、呼び出すことができます。 あなたの開発者とデザイナーが、それを簡単に作成するために、 このアプリケーションで広範囲に選択することは、優れた発想です。詳細は、この項目の、コマンド、トピックを参照してください。
- あなたのView ModelsとModelが、どのようにエラーをビューに報告するかを決定します。 あなたのモデルは、IDataErrorInfoやINotifyDataErrorInfoのどちらかをサポートすることができます。 すべてのモデルが、エラー情報を報告する必要があるわけではありません。 しかし、それらを行うために、あなたの開発者のために、一貫した方法があることは、好ましいです。 詳細については、この項目で、データ検証とエラー報告の項目を参照してください。
- Visual Studio 2013のためのMicrosoft Blendの設計時のデータ・サポートが、 あなたのチームにとって重要かどうか決めます。あなたが、あなたのUIを設計し、維持するために、Blendを使用したい、 そして、設計時にデータを見たいと思う場合、あなたのビューを確認します。 そして、View Modelは、パラメータがないコンストラクタを提供します。そして、あなたのビューは、 設計時のデータ・コンテクストを提供します。他の方法として、設計時の属性を使用して、 Visual Studio 2013のためのMicrosoft Blendにより提供される、d:DataContextとd:DesignSourceのような、 設計時の機能を使用することについて考えてみてください。 詳細については、ユーザー・インターフェイスを構成するの中の、 デザイナーが親しみやすいビューを作成するためのガイドラインを参照してください。
詳細情報
More Information
WPFのデータ結合の詳細については、MSDNのデータ結合(原文リンク)を参照してください。
WPFのコレクションの結合の詳細については、MSDNのデータ結合の概要(原文リンク)の中のコレクションを結合する(原文リンク)を参照してください。
プレゼンテーション・モデル・パターンの詳細については、Martin Fowlerのwebサイトのプレゼンテーション・モデル(原文リンク)を参照してください。
データ・テンプレートの詳細については、MSDNのデータ・テンプレート概要(原文リンク)を参照してください。
MEFの詳細については、MSDNのManaged Extensibility Frameworkの概要(原文リンク)を参照してください。
Unityの詳細については、MSDNのUnityアプリケーション・ブロック(原文リンク)を参照してください。