2021年6月6日日曜日

FAC製作記 その2 ダイアログの表示をどう実装するか

前回に続きダイアログがテーマです。
前回はそもそもダイアログを表示できないところからスタートし、それを解決して表示できるようになりました。
今回はダイアログを表示できるところからスタートしますが、どこでどのように表示するかがポイントになります。
「いや、必要なタイミングで表示させるだけでしょ?」となりそうですが、その表示させる「だけ」にも考えなければならないことはあるわけです。

今回の課題と解法は以下のとおりです。

【課題】
MVVMの役割分担を壊さずにダイアログを表示したい。
.NET Framework時代はMVVMフレームワークの機能を使って実現していたが、.NET移行でライブラリを乗り換えたので、別の方法を探さなければいけない。

【解法】
ヘルパークラスを使う。
ヘルパークラスはM/V/VMいずれでもない名前空間に作る。

本題の前にMVVMについて。
MVVMは「Model」「View」「ViewModel」の頭文字を取ったもので、この3つでアプリケーションを構成します。
それぞれの役割は以下の通り。
  • Model:アプリケーションの本体、UIに関わらない内部の処理
  • View:ユーザーに見える部分、画面とか
  • ViewModel:ModelとViewの橋渡し
以下はWikipediaの図ですが、MとVM、VとVMがやり取りしてアプリの機能を実現します。
(作者:Ugaya40, CC BY-SA 3.0)

例えばFACの「ファイルを分類する」という機能の場合は以下になります。
1. ユーザーがVの実行ボタンをクリックする
2. VからVMに実行ボタンが押されたことが伝わる
3. VMはMのファイル分類を実行する
4. MからVMにファイル分類の結果が返される
5. VMからVにファイルの分類が完了したことが伝わる
6. Vがユーザーにファイルの分類が完了したことを見せる

MVVMは必ず従わなければいけない規則というわけではありません。
しかし、1箇所に全部まとめてしまうとメンテナンスも大変なので、FACでもMVVMパターンを採用しています。

ではMVVM的にダイアログはどう扱うべきでしょうか?
ダイアログはユーザーに見える部分ですから、Vが扱うべきである、と考えることができます。
しかしVは表示とユーザーからの入力を受け付けることが役割ですから、「いつ」「何を」表示するのか決めるのは越権行為です。
「いつ」「何を」表示するか決めるのはM、またはMとVMの役目と言えます。
ではMとVMで扱うのが正しいのかというと、今度はMやVMが直接画面を触るという越権行為が発生します。

もう1点、MVVMの解説図に「Data Binding」というのがあります。
日本語でもそのまま「データバインディング」と言われます。
Vでイベント(例:ボタンのクリック)が発生したときにVMのどの処理を実行するか、Vにどんなものを表示するのか、など、VとVMを紐付ける仕組みです。
MVVMパターンを使う上で重要なポイントではあるのですが、.NETや.NET Framework標準ではこの機能が用意されていません。
ですので、MVVMパターンを使うときは、データバインディングを含めてフレームワークのライブラリを使うことが一般的です。

ではFACでは.NET Framework時代にどうしていたのか、.NETではどうするのか、1つずつ書いていきます。

【.NET Framework時代】
MVVMフレームワーク:MVVM Light Toolkit
MVVM Light Toolkitはその名の通り薄い軽いフレームワークでした。
シンプルですが、データバインディングを始め必要なものは用意されています。

この時代はダイアログの表示をMVVM + Messengerで実現していました。
Messengerはその名の通り、VMからのメッセージをVに渡す役割をします。
VはMessengerに「xxというメッセージが来たらこの処理を呼んでくれ」と登録します。
VMは必要なときにMessengerにxxというメッセージを送ります。
これでVM→(メッセージ)→Messenger→(登録された処理の呼び出し)→Vという流れができます。
なお、本来はMessengerも自分で作らなければならないのですが、MVVM Light ToolkitにはMessengerも用意されています。

今回の場合、VがMessengerに登録する処理でダイアログを表示しています。
こうすることでVMから直接Vを触ることなく、つまりMVVMの役割分担を壊さずにダイアログを表示することができます。

【.NET時代】
MVVMフレームワーク:Prism
Prismはある意味MVVM Light Toolkitとは逆で、盛り沢山なフレームワークです。
機能的にはMVVM Light Toolkitで十分なのですが、最新版のリリースが2018年9月と古く、.NETに対応する様子もないため、.NETへの移行を期にPrismに乗り換えることにしました。

PrismにもMessengerに使えるEventAggregatorが用意されていますが、調べてみるとMVVM Light ToolkitのMessengerと全く同じというわけにはいかないようです。
人によっては「EventAggregatorはVM間の通信を実現する」と言い切っているくらいで、VとVM間の通信に使うのはちょっと怖くなってきました。

ではどうするのか。ここで先に書いた解法です。
FACのフォルダ/名前空間構成は以下のようになっています。
「Models」「ViewModels」「Views」にMVVMのそれぞれにあたるクラスが入ります。
そして、選択して色が変わっている「Helpers」というフォルダがあります。
ここにダイアログを表示するDialogHelperというクラスを入れてあります。(IDialogHelperはDialogHelperで実装する内容を定めたインターフェースです)

ダイアログを表示するときはVMがDialogHelperを呼び出します。
VM→(処理の呼び出し)→Helperという流れですね。
この方式でも、VMから直接Vを触ることなくダイアログを表示することができます。

【残った課題】
Helperクラスを使うことで、「VMが直接ダイアログを表示したりVを触ったりしない」という条件はクリアできました。
しかし「表示はVが受け持つ」という条件はクリアできていません。
.NETに移行するときにはライブラリの乗り換えなど大きな問題が積み重なっていたため、「VMに表示処理が入っていなければヨシ!」と現場猫化しました。
ということで、.NET Framework時代と同じく、ダイアログの表示はVに戻せないか、後日再チャレンジして見るかもしれません。

【参考資料】

0 件のコメント:

コメントを投稿