残念ながら、すべてを実行する優れたMVVMサンプルアプリはありません。また、物事を行うためのさまざまなアプローチがあります。まず、そこにあるアプリフレームワークの1つに慣れることをお勧めします(Prismはまともな選択です)。依存関係の注入、コマンド、イベントの集計などの便利なツールを提供して、自分に合ったさまざまなパターンを簡単に試すことができるからです。 。
プリズムリリース:http :
//www.codeplex.com/CompositeWPF
これには、かなりまともなサンプルアプリ(株式トレーダー)が含まれ、多数の小さなサンプルとその方法が含まれています。少なくとも、MVVMを実際に機能させるために人々が使用するいくつかの一般的なサブパターンの優れたデモンストレーションです。CRUDとダイアログの両方の例があると思います。
Prismは必ずしもすべてのプロジェクトに必要なわけではありませんが、慣れることは良いことです。
CRUD:
この部分は非常に簡単です。WPF双方向バインディングにより、ほとんどのデータの編集が本当に簡単になります。本当のトリックは、UIのセットアップを簡単にするモデルを提供することです。少なくとも、ViewModel(またはビジネスオブジェクト)がINotifyPropertyChanged
バインディングをサポートするように実装し、プロパティをUIコントロールに直接バインドできることを確認する必要がありますがIDataErrorInfo
、検証のために実装することもできます。通常、ある種のORMソリューションを使用する場合、CRUDの設定は簡単です。
この記事では、単純なクラッド操作について説明します。http:
//dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx
これはLinqToSqlに基づいて構築されていますが、例とは無関係です-重要なのは、ビジネスオブジェクトが実装INotifyPropertyChanged
することだけです(LinqToSqlによって生成されるクラスが実装します)。MVVMはその例の要点ではありませんが、この場合は重要ではないと思います。
この記事では、データの検証について説明します
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx
この場合も、ほとんどのORMソリューションは、すでに実装されているクラスを生成しIDataErrorInfo
、通常はカスタム検証ルールを簡単に追加できるようにするメカニズムを提供します。
ほとんどの場合、いくつかのORMによって作成されたオブジェクト(モデル)を取得して、それを保持するViewModelにラップし、保存/削除のコマンドを実行できます。これで、UIをモデルのプロパティに直接バインドする準備が整います。
ビューは次のようになります(Item
ORMで作成されたクラスのように、ViewModelにはモデルを保持するプロパティがあります)。
<StackPanel>
<StackPanel DataContext=Item>
<TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
</StackPanel>
<Button Command="{Binding SaveCommand}" />
<Button Command="{Binding CancelCommand}" />
</StackPanel>
ダイアログ:
ダイアログとMVVMは少し注意が必要です。私はダイアログでMediatorアプローチのフレーバーを使用することを好みます。このStackOverflowの質問でそれについてもう少し読むことができます:
WPF MVVMダイアログの例
私の通常のアプローチは、かなり古典的なMVVMではありませんが、次のように要約できます。
コミットアクションとキャンセルアクションのコマンドを公開するダイアログViewModelの基本クラス、ダイアログが閉じる準備ができていることをビューに通知するイベント、その他すべてのダイアログで必要なもの。
ダイアログの一般的なビュー-これはウィンドウでも、カスタムの「モーダル」オーバーレイタイプコントロールでもかまいません。その中心には、ビューモデルをダンプするコンテンツプレゼンターであり、ウィンドウを閉じるための配線を処理します。たとえば、データコンテキストの変更時に、新しいViewModelが基本クラスから継承されているかどうかを確認できます。関連するcloseイベントにサブスクライブします(ハンドラーがダイアログの結果を割り当てます)。代替のユニバーサルクローズ機能(Xボタンなど)を提供する場合は、ViewModelでも関連するクローズコマンドを実行する必要があります。
ViewModelのデータテンプレートを提供する必要がある場所では、特に、ダイアログごとに個別のコントロールにカプセル化されたビューがあるため、テンプレートは非常に単純です。その場合、ViewModelのデフォルトのデータテンプレートは次のようになります。
<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
<views:AddressEditView DataContext="{Binding}" />
</DataTemplate>
ダイアログビューはこれらにアクセスする必要があります。そうしないと、ViewModelを表示する方法がわからないため、共有ダイアログUIを除いて、その内容は基本的に次のようになります。
<ContentControl Content="{Binding}" />
暗黙的なデータテンプレートはビューをモデルにマップしますが、誰がそれを起動しますか?
これはnot-so-mvvmの部分です。これを行う1つの方法は、グローバルイベントを使用することです。依存関係の注入によって提供されるイベントアグリゲータータイプのセットアップを使用することをお勧めします。これにより、イベントはアプリ全体ではなく、コンテナーに対してグローバルになります。Prismはコンテナーのセマンティクスと依存関係の注入にUnityフレームワークを使用しており、全体としてUnityはかなり好きです。
通常、ルートウィンドウがこのイベントをサブスクライブすることは理にかなっています。ダイアログを開き、発生したイベントで渡されるViewModelにデータコンテキストを設定できます。
これをこのように設定すると、ViewModelはアプリケーションにダイアログを開き、UIについて何も知らなくてもそこでユーザーのアクションに応答するように要求できます。そのため、MVVMの大部分は完全なままです。
ただし、UIがダイアログを表示しなければならない場合があります。たとえば、ダイアログの位置がそれを開くボタンの場所に依存する場合を考えてみます。この場合、ダイアログのオープンを要求するときに、UI固有の情報が必要です。通常、ViewModelといくつかの関連するUI情報を保持する別のクラスを作成します。残念ながら、いくつかのカップリングは避けられないようです。
要素の位置データを必要とするダイアログを発生させるボタンハンドラーの擬似コード:
ButtonClickHandler(sender, args){
var vm = DataContext as ISomeDialogProvider; // check for null
var ui_vm = new ViewModelContainer();
// assign margin, width, or anything else that your custom dialog might require
...
ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
// raise the dialog show event
}
ダイアログビューは位置データにバインドし、含まれているViewModelを内部に渡しContentControl
ます。ViewModel自体はまだUIについて何も知りません。
一般にDialogResult
、ShowDialog()
メソッドのreturnプロパティを使用しないか、ダイアログが閉じるまでスレッドがブロックすることを期待しません。非標準のモーダルダイアログは常にそのように動作するとは限らず、複合環境では、イベントハンドラーがそのようにブロックすることを本当に望まないことがよくあります。私はViewModelでこれを処理することを好みます。ViewModelの作成者は、関連するイベントをサブスクライブしたり、コミット/キャンセルメソッドを設定したりできるため、このUIメカニズムに依存する必要はありません。
したがって、このフローの代わりに:
// in code behind
var result = somedialog.ShowDialog();
if (result == ...
私が使う:
// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container
私のダイアログのほとんどはノンブロッキングの疑似モーダルコントロールであり、このようにすることは、回避するよりも簡単に思えるので、私はこの方法を好みます。単体テストも簡単です。