原文「Implementing the MVVM Pattern Using the Prism Library for WPF」
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クラスとそれらの相互作用
The MVVM classes and their interactions
すべての分離されたプレゼンテーション・パターンのように、効果的にMVVMパターンを使用するための鍵は、あなたのアプリケーションのコードを、適切なクラスの要素として考慮するために、そして、これらのクラスが、さまざまな筋書きで相互作用する方法を理解するには、適切な方法を理解することにあります。次の項目では、MVVMパターンのクラスの各々の役割と特徴を説明します。
Viewクラス
The View Class
ビューの役割は、ユーザーが画面で見るものの、構造と外観を定義することです。理想的には、ビューの分離コードには、InitializeComponentメソッドを呼び出すコンストラクタだけが含まれています。場合によっては、分離コードが、複雑なアニメーションのような、あるいは、コードが、直接、ビューの一部の視覚的な要素を操作する必要があるとき、Extensible Application Markup Language (XAML)で表現することが、難しい、あるいは、能率が悪い、視覚的な動作を実装する、UIロジック・コードが含まれているかもしれません。あなたは、あなたが、ユニット・テストが必要となるビューに、どんな論理コードも配置してはいけません。通常、ビューの分離コード内の論理コードは、方法を検証する、UIオートメーションを通して、テストされます。
WPFでは、Viewのデータ結合式は、そのデータ・コンテキストに対して評価されます。MVVMでは、ビューのデータの状況は、View Modelに設定されます。View Modelは、ビューに、プロパティとコマンドの実装を結びつけ、そして、変更通知イベントを通して、状態のどんな変更でもビューに通知します。一般的に、ビューとそのView Modelの間に、1対1のリレーションシップがあります。
通常、ビューは、コントロールから派生した、あるいは、UserControlから派生したクラスです。しかしながら、場合によっては、データテンプレートで表されるビューが表示されるとき、それは、オブジェクトを、視覚的に示すために使用されるUI要素を指定します。データテンプレートを使用して、ビジュアル・デザイナーは、View Modelが、どのようにレンダリングされるかについて、簡単に指定することができます。あるいは、基盤となるオブジェクト自体、あるいは、それを表示するために使用されるコントロールの動作を変更することなく、その既定の視覚的な表現を修正することができます。
データテンプレートは、少しの分離コードも持っていない、ビューと考えることができます。それらは、UIで表示することを要求されるたびに、特定のView Model型と結合するように設計されています。実行時では、データ・テンプレートによって定義されるViewは、自動的にインスタンスを生成され、対応するView Modelに、そのデータ・コンテクストを設定されるでしょう。
WPFでは、あなたは、アプリケーション階層で、View Model型とデータテンプレートを関連付けることができます。WPFは、続いて、それらがUIで表示されるたびに、指定された型のどんなView Modelオブジェクトにでも、自動的に、データテンプレートを適用するでしょう。これは、暗黙のデータ・テンプレートとして知られています。データ・テンプレートは、それを使用するコントロールと一緒に、あるいは、親のViewの外部のリソース・ディクショナリで、インラインで定義することができます。そして、宣言的に、ビューのリソース・ディクショナリに結合されます。
要約すると、Viewには次の重要な特性があります:
- ビューは、ウィンドウ、ページ、ユーザー・コントロール、データテンプレートのような、視覚的な要素です。ビューは、ビューとそれらの視覚的なレイアウトとスタイルに含まれるコントロールを定義します。
- ビューは、そのDataContextプロパティを通して、View Modelを参照します。ビューのコントロールは、プロパティへのデータ結合とView Modelで公開される命令です。
- ビューは、ビューとView Modelの間で、データ結合の動作をカスタマイズするかもしれません。例えば、ビューは、UIで示されるデータをフォーマットするために、値コンバータを使用するかもしれません。あるいは、それは、ユーザーに、追加の入力データ検証を提供するために、妥当性検証規則を使用するかもしれません。
- ビューは、アニメーションやViewモデルの状態変化、あるいは、UIでユーザーとの対話を通して起動されるかもしれない移行のような、UIの視覚的な動作を定義して、処理します。
- ビューの分離コードは、視覚的な動作を実装するために、UIロジックを定義するかもしれません。それは、XAMLで表すのが困難、あるいは、ビューで定義される特定のUIコントロールに直接参照する必要があります。
View Modelクラス
The View Model Class
MVVMパターン内のView Modelは、プレゼンテーション・ロジックとビューのためのデータをカプセル化します。それは、ビューの特定の実装や型について、ビューやどんな知識にも直接の参照を持っていません。View Modelは、プロパティとコマンドを実装します。その、ビューは、変更通知イベントを通して、データ結合とどんな状態の変更でもビューに通知できます。View Modelで提供されるプロパティとコマンドは、UIで提供される機能を定義しますが、ビューは、その機能が、どのようにレンダリングかを決定します。
ViewModelは、必要なモデル・クラスで、Viewの相互作用を調整する役割があります。通常、ビュー・モデルとモデル・クラスの間に、1対多の関係があります。View Modelは、モデル・クラス・ディレクトリをビューに公開することを選択するかもしれません。そのため、ビュー内のコントロールは、それらにディレクトリをデータ結合することができます。この場合、クラス設計する必要があるモデルは、データ結合、そして、関連した変更通知イベントをサポートしています。この筋書きの詳細については、この項目の後にある、データ結合の項目を参照してください。
View Modelは、変換する、あるいは、モデル・データを操作するかもしれません。そのため、それは、ビューで、簡単に利用することができます。View Modelは、特にビューをサポートするために、追加されたプロパティを定義するかもしれません。;これらのプロパティは、通常の(あるいは、追加することができない)モデルの一部でありません。例えば、View Modelは、ビューが簡単に存在するために、2つのフィールドの値を結合するかもしれません。あるいは、それは、最大の長さのフィールドのために、入力の残りの文字の数を計算するかもしれません。また、View Modelは、データの整合性を確保するために、データ確認ロジックを実装するかもしれません。
View Modelは、また、提供するために、UIでViewの論理的状態が使用する外観の変更を定義するかもしれません。Viewは、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に通知します。
- View Modelは、ビューの相互作用をモデルと調整します。それが、ビューとモデルに存在しないかもしれない、実装されるかもしれない追加されたプロパティによって、簡単に利用することができるように、データを変換する、あるいは、操作するかもしれません。また、それは、IDataErrorInfoやINotifyDataErrorInfoインターフェイスを通して、データ検証を実装するかもしれません。
- View Modelは、ビューが、ユーザーに視覚的に表示することができる論理状態を、定義するかもしれません。
備考:ビュー、あるいは、View model? 何度も、特定の機能が発見される場所は、明らかにしないで実装する必要があります。一般的な目安は以下の通りです:画面上で、UIの特別な外観の見え方に関係があります。そして、(たとえ、あなたが、現在、スタイルを更新する予定がないとしても)後でスタイルを更新することができるビューの中に、入れる必要があります。;どんなものでも、アプリケーションの論理的な動作は、View Modelに行く必要があることは重要です。加えて、なぜなら、View Modelは、ビュー内の特定の視覚的な要素の明確な知識を持っていてはいけません。ビジュアル要素をプログラムで操作するコードは、Viewの分離コード内で、View内に、存在する、あるいは、動作に、カプセル化さする必要があります。同様に、データ項目を取り出したり、操作するコードは、View Modelの中に存在する必要があるデータ結合を通じて、ビューの中で、表示されています。
Modelクラス
The Model Class
MVVMパターン内のモデルは、ビジネス・ロジックとデータをカプセル化します。ビジネス・ロジックは、アプリケーションデータの検索と管理、そして、データの整合性と妥当性検証を確実に強要するどんなビジネス・ルールでも確実に作成することに関係する、どんなアプリケーション・ロジックでも、同じように定義されます。再使用の機会を最大にするために、モデルは、どんな使用事例も含むべきではありません。-具体的な、あるいは、ユーザー・タスク-具体的な動作、あるいは、アプリケーション・ロジック。
通常、モデルは、アプリケーションのクライアント側のドメインモデルを表示します。それは、アプリケーションのデータモデルに基づくデータ構造を定義し、そして、ビジネスと妥当性検証論理を何でもサポートします。また、モデルは、データアクセスとキャッシュをサポートするための、コードが含まれているかもしれません。通常、別々のデータリポジトリやサービスが、これに使用されます。多くの場合、モデルとデータ・アクセス層は、ADO.NET Entity Framework、WCFデータサービス、WCF RIAサービスような、データ・アクセスやサービス戦略の一部として、作成されます。
通常、モデルは、ビューに結合することが容易になりる機能を実装します。これは、通常、それが、INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを通して、プロパティと変更が通知されたコレクションをサポートすることを示しています。一般的に、オブジェクトのコレクションを示すモデル・クラスは、INotifyCollectionChangedインターフェイスの実装を提供するObservableCollection
また、モデルは、IDataErrorInfo (あるいは、 INotifyDataErrorInfo)インターフェイスによって、データ確認とエラー報告をサポートするかもしれません。値が変更されるとき、IDataErrorInfoとINotifyDataErrorInfoインターフェイスは、通知するために、WPFのデータ結合を提供します。そのため、UIは更新することができます。また、それらは、UI層で、データ検証とエラー報告のためのサポートを有効にします。
備考: あなたのモデル・クラスが、必要とするインターフェイスを実装しない場合、どうなるでしょうか?時には、あなたは、INotifyPropertyChanged、INotifyCollectionChanged、IDataErrorInfoやINotifyDataErrorInfoインターフェースを実装していない、モデル・オブジェクトで動作する必要があります。それらの場合において、View Modelは、モデル・オブジェクトを包み、そして、必要なプロパティをビューに公開する必要があるかもしれません。これらのプロパティのための値は、モデル・オブジェクトによって、直接、提供されるでしょう。View Modelは、公開するプロパティのために、必要とされるインターフェイスを実装します。そのため、Viewは、簡単に、それらにデータ結合することができます。
モデルは、次に示す、重要な特性を持っています。:
- 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でそれを修正するとき、基盤となるデータを自動的に更新します。
UIを最新の状態に維持するために、データが、View Modelを変更するとき、それは、適切な変更通知インターフェイスを実装する必要があります。それが、データ結合するプロパティを定義する場合、INotifyPropertyChangedインターフェイスを実装する必要があります。View Modelが、コレクションを示す場合、INotifyCollectionChangedインターフェイスを実装する、あるいは、このインターフェイスの実装を提供する、ObservableCollection
多くの場合、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は、おそらく、更新されるでしょう。TransactionInfoプロパティのセッターで、TickerSymbolプロパティのOnPropertyChangedを呼び出すことによって、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つ以上のプロパティを定義するかもしれません。いずれにせよ、あなたが、ListBoxやビュー内のDataGridコントロールのような、ItemsControlで、コレクションを表示したくなるかもしれません。これらのコントロールは、コレクション示す、、あるいは、ItemSourceプロパティ経由で、コレクションを返す、プロパティをView Modelに結合することができます。
<DataGrid ItemsSource="{Binding Path=LineItems}" />
コレクションを表示する場合、適切にView ModelやModelクラスが要求する変更通知をサポートするために、(INotifyPropertyChangedインターフェイスに加えて)INotifyCollectionChangedインターフェイスを実装しなければなりません。View ModelやModelクラスがプロパティを定義する場合、返されるコレクション・クラスが、INotifyCollectionChangedインターフェイスを実装する必要があるコレクションに参照を返します。
しかしながら、コレクション内の項目が、追加される、削除される、変更されるとき、通知を提供する必要があるため、INotifyCollectionChangedインターフェイスを実装することが、困難な場合があります。それは、多くの場合、インターフェイスを実装しているディレクトリの代わりに、使用する、あるいは、既に、それを実装している、コレクション・クラスから派生することが、さらに、簡単です。ObservableCollection<T>クラスは、このインターフェイスの実装を提供します。そして、一般的に、基底クラスか項目のコレクションを表す実装プロパティのどちらかとして、使用されます。
あなたが、データ結合のためのビューに、コレクションを提供する必要がある、そして、あなたが、ユーザーの選択を追跡する、あるいは、フィルタリング、並べ替えやコレクション内の項目のグループ化をサポートする必要がない場合、あなたは、単純に、ObservableCollection<T>インスタンスに参照を返す、View Modelでプロパティを定義することができます。
public class OrderViewModel : BindableBase
{
public OrderViewModel( IOrderService orderService )
{
this.LineItems = new ObservableCollection<OrderLineItem>(
orderService.GetLineItemList() );
}
public ObservableCollection<OrderLineItem> LineItems { get; private set; }
}
あなたが、コレクション・クラスの参照を取得する場合(たとえば、他のコンポーネントやINotifyCollectionChangedを実装しないサービスから)、あなたは、多くの場合、IEnumerable<T>やList<T>パラメータを取得するコンストラクタの1つを使用して、ObservableCollection<T>インスタンスで、そのコレクションを包むことができます。
備考: BindableBaseは、Prism.Core NuGetパッケージに配置される、Prism.Mvvm名前空間で見つかります。
ICollectionViewを実装する
Implementing ICollectionView
前述のコードの例は、ビューで、コントロールに結合されたデータを通して表示することができる項目のコレクションを返す、単純なView Modelプロパティを、どのように実行するかを示します。ObservableCollection
しかしながら、あなたは、多くの場合、ビュー内で表示される項目のコレクションを、さらに詳細に制御することが必要だと思います。あるいは、その中の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の特定のコントロールは、本質的に、コマンドをサポートして、Commandプロパティを提供します。それは、View Modelで、提供されるICommandオブジェクトにデータ結合することができます。他の場合には、コマンドの動作は、View Modelによって提供される、コマンドのメソッドやコマンドのオブジェクトを、コントロールに関連付けるために使用することができます。
備考: ビヘイビアは、強力で柔軟な拡張の仕組みです。それは、相互作用ロジックと動作をカプセル化するために使用することができます。それは、続いて、ビューでコントロールに宣言的に関連付けることができます。コマンドの動作は、コントロールで、コマンド・オブジェクトやメソッドを、関連付けるために使用することができます。それは、コマンドと相互作用するように特に設計されていませんでした。
次のセクションは、あなたのビューで、コマンドを、コマンド・メソッドとして、または、コマンド・オブジェクトとして、どのように実装するか、そして、ビューでコントロールを、どのように関連付けるかを説明します。
作業に基づいたデリゲート・コマンドを実装する
Implementing a Task-Based Delegate Command
コマンドは、UIスレッドをブロックすることができない、実行時間の長いトランザクションで、コードを呼び出す多くの筋書きがあります。これらの筋書きのために、あなたは、DelegateCommandクラスの、FromAsyncHandlerメソッドを使用する必要があります。それは、非同期ハンドラー・メソッドからのDelegateCommandの新しいインスタンスを作成します。
// 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.Core NuGetパッケージに配置される、Prism.Commands名前空間で見つかります。
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))
{
...
}
}
例えば、次のコード例は、デリゲートを、OnSubmitとCanSubmitのView Modelメソッドに指定することで、Submitコマンドを示すDelegateCommandインスタンスを、どのように、構築するかを示します。コマンドは、続いて、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は、DelegateCommandsが、実行するために互いにグループ化できるCompositeCommandクラスを提供します。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パターンは、効果的にMVVMパターンを使用して、適切なクラスに適切なコードを実装するため、あなたが、あなたのUIをあなたのプレゼンテーションとビジネス・ロジックとデータから、きれいに分離するに役立ちます。また、データ結合とコマンドを通して、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 ロケーターを使用して、View Modelを作成する
Creating the View Model Using a View Model Locator
View Modelロケーターを使用して、View Modelのインスタンスを作成する、そして、そのViewで、それに関連付ける他の方法は、 ViewModelLocationProviderクラス内で、ビューのためのView Modelを解決するために、AutoWireViewModelChangedメソッドの呼び出しを設定するとき、PrismのView Modelロケーターは、AutoWireViewModel添付プロパティを持っています。デフォルトで、それは、規則に基づく方法を使用します。
基本的なMVVMクイックスタートでは、MainWindow.xamlは、View Modelを解決するView Modelロケーターを使用します。
<Window x:Class="QuickStart.Views.MainWindow"
...
xmlns:prism="http://prismlibrary.com/"
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は、そのビューは、.Views child名前空間にある.ViewModels child名前空間にある、ビュー型として、同じアセンブリ内にある事を前提とします。そして、View Model名は、View名と「ViewModel.」の末端に一致します。PrismのView Model Locator規則を、どのように変更するかの説明のために、付録D:Prismの拡張を参照して下さい。
備考: ViewModelLocationProviderは、Prism.Core NugetパッケージのPrism.Mvvm名前空間で見つかります。 ViewModelLocatorは、Prism.WPFNugetパッケージのPrism.Mvvm名前空間で見つかります。
データ・テンプレートとして定義されるビューを作成する
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から、コマンドを公開する場合、決定します。コマンド・メソッドは、簡単に公開でき、ビュー内で動作を通して呼び出すことができます。Commandオブジェクトは、コマンドと有効/無効ロジックをきちんとカプセル化することができます。そして、動作を通じて、あるいは、ButtonBaseから派生したコントロールのCommandプロパティを通じて、呼び出すことができます。あなたの開発者とデザイナーが、それを簡単に作成するために、このアプリケーションで広範囲に選択することは、優れた発想です。詳細は、この項目の、コマンドの項目を参照してください。
- あなたのView ModelsとModelが、どのようにエラーをビューに報告するかを決定します。あなたのモデルは、IDataErrorInfoやINotifyDataErrorInfoのどちらかをサポートすることができます。すべてのモデルが、エラー情報を報告する必要があるわけではありませんが、それを行うための、あなたの開発者のために、一貫した方法があることは、好ましいです。詳細については、この項目で、データ検証とエラー報告の項目を参照してください。
- Visual Studio 2013のためのMicrosoft Blendの設計時のデータ・サポートが、あなたのチームにとって重要かどうか決めます。あなたが、あなたのUIを設計し、維持するために、Blendを使用したい、そして、設計時にデータを見たいと思う場合、あなたのViewとView Modelが、パラメーターを持っていない、コンストラクタを提供することを確認してください。そして、あなたのビューは、設計時のデータ・コンテクストを提供します。他の方法として、設計時の属性を使用して、Visual Studio 2013のためのMicrosoft Blendにより提供される、d:DataContextとd:DesignSourceのような、設計時の機能を使用することについて考えてみてください。詳細については、「ユーザー・インターフェイスを構成する」内の「デザイナーが、扱いやすいViewを作成することのためのガイドライン」を参照してください。
詳細情報
More Information
- WPFのデータ結合の詳細については、MSDNのデータ結合を参照してください。
- WPFのコレクションの結合の詳細については、MSDNのデータ結合の概要の中のコレクションを結合するを参照してください。
- プレゼンテーション・モデル・パターンの詳細については、Martin Fowlerのwebサイトのプレゼンテーション・モデルを参照してください。
- データ・テンプレートの詳細については、MSDNのデータ・テンプレートの概要を参照してください。
- MEFの詳細については、MSDNのManaged Extensibility Frameworkの概要を参照してください。
- Unityの詳細については、MSDNのUnityアプリケーション・ブロックを参照してください。
- DelegateCommandとCompositeCommandの詳細については、疎く結合したComponentの間で情報をやりとりするを参照してください。