はじめに
GUIを含んだアプリケーションのアーキテクチャにMVVMモデルがあります。 自分でWindowsデスクトップアプリケーションを作るにあたり、正しくMVVMモデルに則ったクラス設計をしたいと思いました。 しかしテンプレートのプロジェクトを立ち上げた時点で作成されるクラスがMVVMでいうところの何に当てはまるのか分からなかったので調査しました。 幸い、Microsoftの公式リポジトリに参考になるプロジェクトを発見したので、そのアプリのアーキテクチャを見ていきたいと思います。
分析するリポジトリ
Windows-appsample-customers-orders-databaseです。 以下のページでMVVMが採用されているサンプルとして紹介されていました。
顧客リストと注文リストの内容を画面に表示するアプリですが、内容にはあまり触れず、クラスの構成を見ていきます。
パッケージ構成
View, UserControl, ViewModelが綺麗に整理されていることが確認できます。 Serviceパッケージもありますが、どのような責務を担っているのか気になるところです。
ちなみにContosoとはMicrosoftがサンプル用に使う架空の会社名だそうです。(初めて知った。)
Viewクラス
ViewsディレクトリとContosoApp直下の一部のクラスをモデルに起こすと以下のようになります。
トップにMainWindowがあり、顧客情報一覧と受注情報一覧を見るページがそれぞれあるようです。
Viewのクラス群の実装を確認すると次のようなことが分かります。
- xamlファイル内のアプリ実行中に動的に変更される部分はすべてバインドされている。
- 静的な値はApp.xamlに定義して各ページから参照する
- 処理はViewModelのメソッドを呼ぶかページ更新
- デリゲートの引数を活用している。
- Appクラスへの参照はOK
xamlファイル内のアプリ実行中に動的に変更される部分はすべてバインドされている。
例えばCustomerDetailPage.xaml
では
<AppBarButton Click="{x:Bind ViewModel.StartEdit}" Icon="Edit" IsEnabled="{x:Bind vm:Converters.Not(ViewModel.IsInEdit), Mode=OneWay}" Visibility="{x:Bind vm:Converters.CollapsedIf(ViewModel.IsNewCustomer), Mode=OneWay}" Label="Edit" />
動的に変化する部分は全てbindされています。 クリックイベントに関しては私はこれまで直接メソッド名をここに書いて実装しており、依存関係的にも問題ないと思っていたので、bindする価値がどのようなところにあるのかは疑問です。
静的な値はApp.xamlに定義して各ページから参照する
<!-- CustomerDetailPage.xaml --> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="{StaticResource LargeWindowSnapPoint}" /> </VisualState.StateTriggers> <!-- App.xaml --> <x:Double x:Key="LargeWindowSnapPoint">1008</x:Double>
グローバル変数みたいな使い方ですね。
処理はViewModelのメソッドを呼ぶかページ更新
MVVMのお手本はこんな感じなのかと関心した部分です。 Viewクラスは自身のメンバにViewModelインスタンスを持っておき、イベントが発生するたびにViewModelの必要なメソッドを実行します。
画面への表示に関しては何も記述されていません。 後述しますが画面に表示する内容はViewModelクラスの変数にバインドされているので、Viewのクラスでは全く現れません。
Appクラスへの参照はOK
意外だったところで、PageクラスからAppクラスを参照している箇所がありました。
var customer = App.ViewModel.Customers.Where(cust => cust.Model.Id == guid).FirstOrDefault();
Appクラスはエントリーポイント的なイメージで、ここを参照しているのは見たことはありませんでした。やっていいんだ。 というかなんで参照できてるのかわかってない部分があります。
ViewModelクラス
次にViewModelを見ていきます。 MVVMの考え方でいけば、Modelが持つ情報を加工したりする人です。
ViewとViewModelの関係の部分を取り上げてクラス図を描きました。 まず、ViewModelにstringを返すメソッドが多いです。これはViewにバインドされているメソッドで、ViewModel内の変数が更新されれば勝手にViewのほうも更新されるものです。
ViewModelのクラス群の実装を確認すると次のようなことが分かります。 - 「選択中のモデル」のような特別なModelの情報を持つ - 「編集中」のような一時的な状態を持つ
「選択中のモデル」のような特別なModelの情報を持つ
このアプリで起こるシチュエーションとして、画面上のリストの中から1つの項目を選択した状態で詳細ページを開くというものがあります。 この、選択中の項目の情報を管理する役割をViewModelが担っています。
「編集中」のような一時的な状態を持つ
GUI上の操作のシーケンスに応じて状態を管理したいケースがあります。そしてその状態を使用してGUIの状態を操作したいケース、例えばModelの編集中はあるボタンを押せないようにしたいといったときに使うような状態はViewModelクラスで管理されています。
Modelクラス
長くなったので分けます