MVVMアプリケーションでナビゲーションを制御する必要があるのは誰ですか?


33

例#1:MVVMアプリケーションにビューが表示され(ディスカッションの目的でSilverlightを使用しましょう)、新しいページに移動するボタンをクリックします。

例#2:同じビューに別のボタンがあり、クリックすると、子ウィンドウ(ダイアログ)に詳細ビューが開きます。

ユーザーのクリックに応答するメソッドを持つボタンにバインドされたViewModelによって公開されるCommandオブジェクトがあることを知っています。しかし、それではどうでしょうか?どうすればアクションを完了できますか?いわゆるNavigationServiceを使用していても、何を伝えているのでしょうか?

具体的には、従来のビュー優先モデル(WebやSL組み込みナビゲーションフレームワークなどのURLベースのナビゲーションスキームなど)では、Commandオブジェクトは次に表示するビューを知る必要があります。パターンによって促進される懸念の分離に関しては、これは境界線を越えるようです。

一方、ボタンがCommandオブジェクトに接続されておらず、ハイパーリンクのように動作する場合、ナビゲーションルールはマークアップで定義できます。しかし、ビューでアプリケーションのフローを制御し、ナビゲーションは単なる別のタイプのビジネスロジックではありませんか?(場合によってはyes、その他の場合はnoと言うことができます。)

私にとって、MVVMパターンのユートピア実装(および他の人がこれを公言していると聞いたことがあります)は、アプリケーションがヘッドレスで実行できるように(つまり、ビューなし)ViewModelを配線することです。これにより、コードベースのテストに最も広い領域が提供され、ビューがアプリケーションの真のスキンになります。そして、私のViewModelは、メインウィンドウ、フローティングパネル、または子ウィンドウに表示されるかどうかを気にするべきではありませんか?

このアプローチによると、各ViewModelに表示するビューを「バインド」するのは、実行時に他の何らかのメカニズムに依存します。しかし、ビューを複数のViewModelと共有したい場合、またはその逆の場合はどうでしょうか?

したがって、View-ViewModel関係を管理する必要があるため、子ウィンドウ/ダイアログの表示など、ビュー間をナビゲートする必要がある場合に何を表示するかを知るために、MVVMパターンでこれをどのように実現しますか?

回答:


21

ナビゲーションは常にViewModelで処理する必要があります。

MVVM設計パターンの完全な実装は、Viewsなしでアプリケーションを完全に実行できることを意味し、ViewsがNavigationを制御している場合は実行できないことを考えると、あなたは正しい軌道に乗っています。

通常、アプリケーションの全体的な状態を処理するApplicationViewModel、またはShellViewModelがあります。これには、CurrentPage(ViewModel)と処理用のコードが含まれChangePageEventsます。(CurrentUserやErrorMessagesなど、他のアプリケーション全体のオブジェクトにもよく使用されます)

したがって、任意のViewModelがどこでもをブロードキャストするChangePageEvent(new SomePageViewModel)場合、ShellViewModelそのメッセージがピックアップCurrentPageされ、メッセージで指定されたページに切り替えられます。

興味があるなら、実際にMVVMでのナビゲーションに関するブログ記事を書きました


2
興味深いアプローチ。4つのコメント:1)SilverlightはDataTemplateのDataTypeプロパティをサポートしていないため、DataTemplateをViewModelにマッピングすることはSLではできません。2)これは、ViewとViewModelの間の多対多の可能性に対処していません。3)子ウィンドウは処理しません(少なくとも、どのように表示されるかはわかりません)。4)アプリケーション/シェルViewModelとその子(孫など)の間の密結合が必要です。アプリに40ページがある場合、このViewModelは管理が面倒です。
SonOfPirate

とはいえ、それは間違いなく検討すべきものです。
SonOfPirate

@SonOfPirate 1)Silverlightは(まだ)暗黙のDataTemplateマッピングをサポートしていませんが、aをサポートしていDataTemplateSelectorます。これは、Silverlightアプリで通常使用するものです。2)以前に多対多の状況でこれを使用しました。たとえば、1つのViewModelに複数のビューを含めることも、1つのViewを複数のViewModelに関連付けることもできます。前者の例については、rachel53461.wordpress.com / 2011/05/28 /…を参照してください。後者の場合、複数のViewModelがDataTemplateSelectorの同じビューにマップされるように指定する必要があります。
レイチェル

3)それは単なる基本的な例です。アプリケーションが複数のウィンドウになることを知っていた場合、ShellViewModelを変更して複数のウィンドウを処理することは明らかですCurrentPages4)繰り返しますが、これは単なる基本的な例です。実際には、PageViewModelsはすべていくつかの基本クラスに基づいているため、ShellViewModelはなどの基本クラスまたはインターフェイスでのみ動作しますIPageViewModel。本当に最大の厄介なマッピングは、40ビューを40 ViewModelsにマッピングする必要があるDataTemplateSelectorです。
レイチェル

@SonOfPirateあなたの質問のいくつかに答えたことを望みます。他の人がいればチャットで気軽に調べてください:)
レイチェル

6

閉鎖のために、私はこの問題を解決するために最終的に選んだ方向を投稿すると思いました。

最初の決定は、すぐに使用できるSilverlight Page Navigationフレームワークを活用することでした。この決定は、このタイプのナビゲーションがMicrosoftによってWindows 8 Metroアプリに引き継がれており、Phone 7アプリのナビゲーションと一貫しているという知識を含むいくつかの要因に基づいています。

それを機能させるために、次にASP.NET MVCが規約ベースのナビゲーションで行った作業を見てみました。FrameコントロールはURIを使用して、表示する「ページ」を見つけます。類似性は、Silverlightアプリで同様の規則ベースのアプローチを使用する機会を提供しました。トリックは、MVVMの方法ですべてを連携させることでした。

ソリューションはNavigationServiceです。このサービスは、ViewModelsがページ変更を開始するために使用できるNavigateToやBackなどのいくつかのメソッドを公開します。新しいページが要求されると、NavigationServiceはMVVMLight Messenger機能を使用してCurrentPageChangedMessageを送信します。

Frameコントロールを含むビューには、このメッセージをリッスンしているDataContextとして設定された独自のViewModelがあります。受信すると、新しいビューの名前が、マッピング規則を使用して規則に適用され、CurrentPageプロパティに設定されます。FrameコントロールのSourceプロパティは、CurrentPageプロパティにバインドされています。その結果、プロパティを設定するとソースが更新され、ナビゲーションがトリガーされます。

NavigationServiceに戻ります。NavigateToメソッドは、ターゲットページの名前を受け入れます。ViewModelにUIの懸念がないことを確認するために、使用される名前は表示するViewModelの名前です。実際に、ヘルパーとしてナビゲート可能な各ViewModelのフィールドを持つ列挙を作成し、アプリ全体のマジックストリングを削除しました。上記のマッピング関数は、名前から「ViewModel」接尾辞を取り除き、名前に「Page」を追加し、フルネームを「Views {Name} Page.xaml」に設定します。

したがって、たとえば、顧客の詳細ビューに移動するには、次のように呼び出すことができます。

NavigationService.NavigateTo(ViewModels.CustomerDetails);

CustomerDetailsの値は、「Views \ CustomerDetailsPage.xaml」にマッピングされる「CustomerDetailsViewModel」です。

このアプローチの利点は、UIがViewModelsから完全に切り離されているにもかかわらず、完全なナビゲーションがサポートされていることです。ただし、コードを変更しなくても、アプリケーションのスキンを変更できます。

説明が役立つことを願っています。


2

Rachelが言ったことと同様に、私のほとんどMVVMアプリケーションは、Presenterウィンドウまたはページ間の切り替えを処理する必要があります。レイチェルはこれをと呼びますApplicationViewModelが、私の経験では、通常、単なるバインディングターゲット(メッセージの受信、Windowsの作成など)を行う以上のことを行う必要があるため、技術的には従来のPresenterまたはに似ていControllerます。

私のアプリケーションでは、でPresenter始まりCurrentViewModelます。Presenter間のすべての通信傍受ViewViewModelViewModel対話中にできることの1つは、newを返すことですViewModel。これは、新しいページまたは新しいページを表示することを意味しWindowます。Presenter作成または上書きの世話をするView新しいためViewModelと設定しますDataContext

アクションの結果として、a ViewModelが「完了」することもあります。その場合、Presenterこれはこれを検出してウィンドウを閉じるかViewModel、VMスタックからポップして前のページの表示に戻ります。


プレゼンターはどのビューを表示するのかをどのように知るのですか?
SonOfPirate

1
@SonOfPirate-これは通常、WPFメカニズムを介して行われます。Presenterただスティックは、返されたViewModelビジュアルツリーで、とWPFは、適切なグラブView、フックしDataContext、そのビジュアルツリー内の代わりに、とプットを。これを行うには、レンダリングする型DataTemplateを宣言するsを使用するかViewModel、カスタムデータテンプレートセレクターを作成します。それはまだWPF機能の領域内です。
スコットホイットロック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.