例1:MVVMアプリケーションにビューを表示し(ディスカッションの目的でSilverlightを使用しましょう)、新しいページに移動するボタンをクリックします。
例2:同じビューに別のボタンがあり、クリックすると、子ウィンドウ(ダイアログ)に詳細ビューが開きます。
ユーザーのクリックに応答するメソッドを備えたボタンにバインドされたViewModelによって公開されるCommandオブジェクトがあることはわかっています。しかし、それではどうでしょうか。アクションをどのように完了しますか?いわゆるNavigationServiceを使用していても、何を伝えているのでしょうか。
より具体的には、従来のビューファーストモデル(WebまたはSL組み込みナビゲーションフレームワークなどのURLベースのナビゲーションスキームなど)では、コマンドオブジェクトは次に表示するビューを認識している必要があります。パターンによって促進された懸念の分離に関しては、それは一線を越えているようです。
一方、ボタンがCommandオブジェクトに関連付けられておらず、ハイパーリンクのように動作する場合は、ナビゲーションルールをマークアップで定義できます。しかし、ビューでアプリケーションフローを制御し、ナビゲーションが別のタイプのビジネスロジックではないのですか? (私はいくつかのケースでイエスと言うこともあれば、ノーと言うこともできます。)
私にとって、MVVMパターンのユートピア実装(そして私が他の人がこれを公言するのを聞いた)は、アプリケーションがヘッドレス(つまり、ビューなし)で実行できるようにViewModelを配線することです。これにより、コードベースのテストに最も広い領域が提供され、ビューがアプリケーションの真のスキンになります。そして、私のViewModelは、メインウィンドウ、フローティングパネル、または子ウィンドウのどちらに表示されても問題ありませんか?
このアプローチによれば、各ViewModelにどのビューを表示するかを「バインド」するのは、実行時の他のメカニズム次第です。しかし、ビューを複数のViewModelと共有したい場合、またはその逆の場合はどうでしょうか?
したがって、View-ViewModel関係を管理する必要があるので、子ウィンドウやダイアログの表示など、ビュー間を移動する必要があるときに何を表示するかがわかるので、これをMVVMパターンで本当に実現するにはどうすればよいでしょうか。
閉鎖のために、私はこの問題を解決するために最終的に選択した方向を投稿すると思いました。
最初の決定は、標準で提供されているSilverlightページナビゲーションフレームワークを活用することでした。この決定は、このタイプのナビゲーションがMicrosoftによってWindows 8 Metroアプリに引き継がれ、Phone 7アプリのナビゲーションと一貫しているという知識を含むいくつかの要因に基づいていました。
これを機能させるために、次に、ASP.NET MVCがコンベンションベースのナビゲーションで行った作業を確認しました。フレームコントロールは、URIを使用して、表示する「ページ」を見つけます。その類似性により、Silverlightアプリで同様のコンベンションベースのアプローチを使用する機会が提供されました。トリックは、MVVMの方法ですべてを一緒に機能させることでした。
ソリューションはNavigationServiceです。このサービスは、NavigateToやBackなど、ViewModelsがページ変更を開始するために使用できるいくつかのメソッドを公開します。新しいページが要求されると、NavigationServiceはMVVMLightメッセンジャー機能を使用して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から完全に切り離されているにもかかわらず、ナビゲーションが完全にサポートされていることです。ただし、コードを変更することなく、アプリケーションのスキンを変更できます。
説明がお役に立てば幸いです。
ナビゲーションは常にViewModelで処理する必要があります。
MVVM設計パターンの完全な実装はビューなしでアプリケーションを完全に実行できることを意味し、ビューがナビゲーションを制御している場合はそれを実行できないと考えて、順調に進んでいます。
通常、ApplicationViewModel
、またはShellViewModel
を使用して、アプリケーションの全体的な状態を処理します。これには、CurrentPage
(ViewModel)とChangePageEvents
を処理するためのコードが含まれます。 (これは、CurrentUserやErrorMessagesなどの他のアプリケーション全体のオブジェクトにもよく使用されます)
したがって、いずれかのViewModelがChangePageEvent(new SomePageViewModel)
をブロードキャストすると、ShellViewModel
はそのメッセージをピックアップし、CurrentPage
をメッセージで指定されたページに切り替えます。
MVVMでのナビゲーション についてのブログ投稿を実際に書きました
レイチェルが言ったように、私のほとんどのMVVMアプリケーションには、ウィンドウまたはページ間の切り替えを処理するPresenter
があります。レイチェルはこれをApplicationViewModel
と呼んでいますが、私の経験では、一般にバインディングターゲット(メッセージの受信、Windowsの作成など)だけでなく、技術的には従来のPresenter
またはController
。
私のアプリケーションでは、Presenter
はCurrentViewModel
で始まります。 Presenter
は、View
とViewModel
間のすべての通信を傍受します。インタラクション中にViewModel
が実行できることの1つは、新しいViewModel
を返すことです。つまり、新しいページまたは新しいWindow
を表示する必要があります。 Presenter
は、新しいView
のViewModel
を作成または上書きし、DataContext
を設定します。
アクションの結果として、ViewModel
が「完了」する場合もあります。その場合、Presenter
がこれを検出してウィンドウを閉じるか、またはこのViewModel
をポップして= VMスタックし、前のページの表示に戻ります。