MVVMテンプレートの良い例 質問する

MVVMテンプレートの良い例 質問する

現在、Microsoft MVVM テンプレートを使用していますが、詳細な例が不足していることに不満を感じています。付属の ContactBook の例にはコマンド処理がほとんど示されておらず、他に見つけた例は MSDN マガジンの記事のみです。この記事では概念は似ていますが、わずかに異なるアプローチが使用されており、複雑さがまったくありません。少なくとも基本的な CRUD 操作とダイアログ/コンテンツの切り替えを示す適切な MVVM の例はありますか?


皆さんの提案は本当に役立ちました。良いリソースのリストを作成し始めます。

フレームワーク/テンプレート

役に立つ記事

スクリーンキャスト

追加ライブラリ

ベストアンサー1

残念ながら、あらゆることを実行する優れた MVVM サンプル アプリは存在せず、さまざまなアプローチが存在します。まず、アプリ フレームワークの 1 つ (Prism は適切な選択肢です) に慣れておくことをお勧めします。これらのフレームワークには、依存性注入、コマンド、イベント集約などの便利なツールが用意されており、自分に合ったさまざまなパターンを簡単に試すことができます。

プリズムリリース:
http://www.codeplex.com/CompositeWPF

かなり良いサンプル アプリ (株式トレーダー) と、多数の小さなサンプルおよびハウツーが含まれています。少なくとも、MVVM を実際に動作させるために人々が使用するいくつかの一般的なサブパターンの良いデモンストレーションです。CRUD とダイアログの両方のサンプルがあると思います。

Prism は必ずしもすべてのプロジェクトに適しているわけではありませんが、慣れておくと良いでしょう。

CRUD:この部分は非常に簡単です。WPF の双方向バインディングにより、ほとんどのデータの編集が非常に簡単になります。本当の秘訣は、UI の設定を簡単にするモデルを提供することです。少なくとも、ViewModel (またはビジネス オブジェクト) がINotifyPropertyChangedバインディングをサポートするように実装されていること、およびプロパティを UI コントロールに直接バインドできることを確認する必要がありますが、検証用に実装することも必要ですIDataErrorInfo。通常、何らかの ORM ソリューションを使用する場合、CRUD の設定は簡単です。

この記事では、単純な 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ビューは次のようになります (ViewModel には、ORM で作成されたクラスのようなモデルを保持するプロパティがあります)。

<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 が基本クラスから継承されているかどうかを確認し、継承されている場合は、関連する閉じるイベントをサブスクライブします (ハンドラーがダイアログの結果を割り当てます)。代替のユニバーサル閉じる機能 (たとえば、X ボタン) を提供する場合は、関連する閉じるコマンドを ViewModel でも実行する必要があります。

ViewModel のデータ テンプレートを提供する必要がある場合もありますが、各ダイアログのビューが個別のコントロールにカプセル化されている可能性が高いため、データ テンプレートは非常にシンプルになります。ViewModel の既定のデータ テンプレートは次のようになります。

<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
   <views:AddressEditView DataContext="{Binding}" />
</DataTemplate>

ダイアログ ビューはこれらにアクセスできる必要があります。そうしないと、ViewModel を表示する方法がわかりません。共有ダイアログ UI を除いて、その内容は基本的に次のようになります。

<ContentControl Content="{Binding}" />

暗黙的なデータ テンプレートはビューをモデルにマップしますが、誰がそれを起動するのでしょうか?

これはあまり 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の return プロパティを使用しShowDialog()たり、ダイアログが閉じられるまでスレッドがブロックされることは想定していません。非標準のモーダル ダイアログは常にそのように動作するとは限らず、複合環境では、イベント ハンドラーがそのようにブロックされることは望ましくないことがよくあります。私は 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

私のダイアログのほとんどは非ブロッキング疑似モーダル コントロールであり、この方法の方が回避策よりも簡単なので、この方法を好みます。単体テストも簡単です。

おすすめ記事