WPF Prism episode: 15 ~ FolderBrowserDialog の為ならば、Prism の InteractionRequest はもしかしたらコモンダイアログも Popup できるかもしれない。 ~

← 前回記事【WPF Prism episode: 14 ~ ListBox 相手は ReactiveCollection、ダイアログな、Prism。 ~】

Prism 入門 episode シリーズは 15 本目の今回で最終回です。
そして最終回となる episode: 15 は Prism から MVVM パターンで『ファイルを開くダイアログ』等のコモンダイアログを表示する方法を紹介します。

尚、この記事は Visual Studio 2019 Community Edition で .NET Framework 4.7.2 以上 と C# + Prism 7.1 + ReactiveProperty + Extended WPF Toolkit™ を使用して 、WPF アプリケーションを MVVM パターンで作成するのが目的で、C# での基本的なコーディング知識を持っている人が対象です。

コモンダイアログを MVVM パターンで表示する

『ファイルを開くダイアログ』や『名前を付けて保存ダイアログ』等のコモンダイアログを表示する場合、WPF 標準では src. 1 のように ShowDialog で表示できます。

ですが、episode: 12 で紹介したメッセージボックスを表示する場合と同じく MVVM パターンでは VM からの ShowDialog は歓迎されないので、Prism の InteractionRequest と Notification の仕組みを応用してコモンダイアログを表示する方法を紹介します。

コモンダイアログを表示するための Action と Confirmation

episode: 12 で紹介した通り InteractionRequestTrigger からメッセージボックスを表示するには src. 1 のように XAML へ PopupWindowAction を指定します。

又、episode: 13 でも紹介しましたが、PopupWindowAction は Window を自前で Load するためコモンダイアログ等の表示には使用できません。

そのためコモンダイアログ表示用 Action のサンプルを探すと『MVVM pattern and dialogs – Plainionist Journey of a Software Craftsman』で紹介されていました!
note コモンダイアログは『System Dialog』のタイトルで上記ページの最後辺りで紹介されています。

基本的には上記ページで紹介されている File open/save dialog action をほぼそのまま流用したのが src. 2 の PopupCommonDialogAction です。

src. 2 の PopupCommonDialogAction は TriggerAction<FrameworkElement> を継承して、表示するダイアログを指定するための CommonDialogType 依存プロパティを定義しています。

TriggerAction の virtual に指定されている Invoke メソッドは以下の前提で書かれています。

  • InteractionRequest のパラメータに IConfirmation を継承したクラスがセットされること
  • IConfirmation の実装クラスは表示するコモンダイアログと同名のプロパティが定義されていること

そして処理内容は src. 2 内のコメント通りです。

ShowDialog 前後のプロパティ値コピーにリフレクションを使用しているためパフォーマンスはあまり良くないと思いますが、この Action 1 つで CommonDialog を継承したシステムダイアログ全てに対応できます

この連載ではお馴染みのかずきさんも同じ方法を『MVVMでファイルを開くダイアログを使う(View完結とViewModel経由) – かずきのBlog@hatena』で紹介されていて、管理人も前に見かけたことはありましたが、今回もう 1 度見るまで同じ方法だと気が付きませんでした。
結構古い記事(2019/5/17 現在から 8 年前…)なのでこの連載で紹介しているバージョンの Prism とは少し違う部分もありますが一応紹介しておきます。

そして、VM からセットする IConfirmation の実装クラスは表示するコモンダイアログと同じプロパティをオブジェクトブラウザ等を見て定義しているだけなので GitHub リポジトリ上で直接見てください。
言い訳 ドキュメントコメントの日本語が変だと思いますが、大半はオブジェクトブラウザからコピーしただけなので大目に見てください

ここでは OpenFileDialog 用の Confirmation しか作成していませんが、FileSelectCommonDialogConfirmationBase を継承して SaveFileDialogConfirmation を作成すれば SaveFileDialog も表示できます

又、episode: 12 で紹介したメッセージボックス表示 Service を真似て src. 3 のようなコモンダイアログ表示 Service を作成しておくと Windows Form でコモンダイアログを表示する場合と同じような記述にできますし、他の Window や View、プロジェクトへも使い回せるのでお勧めです。

又、MVVM パターンの利点の 1 つであるユニットテストを実行し易くするための Service 用のインタフェースも作成しているのでソースコードは GitHub リポジトリ で見てください。

但し、episode: 12 で紹介したメッセージボックス用のサービスは 1 つで何パターンものメッセージボックスに対応できますが、このコモンダイアログ用サービスは src. 3 のままでは 1 つのコモンダイアログに対して 1 つのサービスが必要な構造なので注意が必要です。

尚、今回作成した全てのクラスは新規で追加した PrismCommonDialogLib プロジェクト内へ作成しているので他のプロジェクトでも簡単に使用できます
PrismCommonDialogLib プロジェクトを新規作成する場合、Prism や System.Windows 等の参照が必要なため、Prism Module テンプレートから作成すると後から参照を追加する面倒が少なくて済むのでお勧めです。

Prism の InteractionRequestTrigger を経由してコモンダイアログを表示する

コモンダイアログを表示するための XAML も episode: 12 で紹介したメッセージボックス呼出部分とほとんど同じで src. 4 のように指定します。

InteractionRequestTrigger は episode: 12 で紹介した通り View の Action を呼び出すための InteractionRequest を指定します。
実行する Action には先ほど作成した PopupCommonDialogAction を指定して、CommonDialogType には表示するコモンダイアログの Type も併せて指定します。

又、PopupCommonDialogAction と OpenFileDialog を XAML から参照するために『Microsoft.Win32』名前空間と『PrismCommonDialog』のエイリアスも追加しています。

InteractionRequestTrigger をバインドする MainWindow の VM を src. 5 のように変更します。

面倒なので とりあえず表示するだけなので OpenFileDialogConfirmation には Title しか設定していませんが、通常のアプリであれば Filter プロパティ等の設定は必須だと思います。
実際に使用する場合は OpenFileDialogConfirmation を初期化している部分へプロパティ設定を追加すると指定通りのコモンダイアログが表示できるはずです。

コモンダイアログ表示用の Service は episode: 12 で紹介したメッセージボックス表示用 Service と同じく DI コンテナの Unity からコンストラクタへインジェクションしてもらうために、src. 6 のように App.xaml.cs へ Service を登録するための処理も併せて追加しています。

この時点でサンプルアプリを起動して、MainWindow の『MVVMパターンでShowDialog』ボタンを Click すると fig. 1 のように『ファイルを開くダイアログ』が表示され、選択したファイルのパスも取得できます。

fig.1 ファイルを開くダイアログを表示

ここまで記述していれば、コモンダイアログを表示するだけなら、src. 4 の CommonDialogType(11 行目)を変更するだけで『ファイルを開くダイアログ』、『名前を付けて保存ダイアログ』のどちらでも表示することができます

Prism から MVVM パターンでフォルダ参照ダイアログを表示する

WPF には『ファイルを開くダイアログ』や『名前を付けて保存ダイアログ』は用意されていますが、Windows Form で呼び出せていた fig. 2 の FolderBrowserDialog WPF 版は用意されていません。

fig.2 FolderBrowserDialog

WPF でも System.Windows.Forms を参照に追加すれば呼び出せますが、何となく気持ち悪い… と言うような理由で紹介されている情報も多くありませんし、最近(2019/5/16 現在)では fig. 2 のフォルダ参照ダイアログではなく、Visual Studio でソリューションの保存先を指定する場合等に表示される fig. 3 のような『ファイルを開くダイアログ』ベースのフォルダ選択ダイアログが主流になってきていると思います。

fig.3 フォルダ選択ダイアログ

以降では、今回作成した PopupCommonDialogAction から fig. 3 のようなフォルダ選択ダイアログを表示する方法を紹介します。

Windows API Code Pack の CommonOpenFileDialog

WPF からフォルダ選択ダイアログを表示する方法を検索すると、Windows API Code Pack を利用してコードビハインドから表示する方法が上位に多く表示されます。
Windows API Code Pack に含まれる CommonOpenFileDialog も今回作成した PopupCommonDialogAction から表示する事は一応できますが、ここでの紹介は見送ります。

見送った言い訳 理由は以下のような状況からです。

  • Windows API Code Pack の CommonOpenFileDialog は CommonDialog クラスを継承していない
  • 使用頻度が高いと予想される Filters プロパティの型が読み取り専用の Collection
  • Filters プロパティの型である CommonFileDialogFilterCollection のコンストラクタは internal なので外部で new できない
  • ダイアログでキャンセルした後 FileName プロパティにアクセスすると例外が発生する

1 つ 1 つは大したことがないので PopupCommonDialogAction を修正すれば対応できそうですが、プロパティのコピー部分で Filters プロパティだけはプロパティ名で直接指定して処理するくらいしか思いつかず、例として紹介するのはイマイチな気がしたので見送る事にしました。

Ookii.Dialogs とは

Windows API Code Pack 以外でフォルダ選択ダイアログを表示するには Ookii.Dialogs を使用する方法があります。
Ookii.Dialogs は『フォルダ選択ダイアログ』を始めとして『ファイルを開くダイアログ』や『タスクダイアログ』、『プログレスダイアログ』等 7種類 のシステムダイアログが含まれているライブラリですが、今回作成した PopupCommonDialogAction から表示できるダイアログは以下の 3 つです。

  • VistaOpenFileDialog(ファイルを開くダイアログ)
  • VistaFolderBrowserDialog(フォルダ選択ダイアログ)
  • VistaSaveFileDialog(名前を付けて保存ダイアログ)

Ookii.Dialogs の導入

Ookii.Dialogs を導入するには GitHub の vsvyatski/Ookii.Dialogs リポジトリ からソリューションを Clone 又は Download します。
上で紹介した公式サイト ではなく GitHub から取得する必要があるので気を付けてください
notice! GitHub からプロジェクトを取得する方法はここでは紹介しません。

取得したリポジトリ内の『Ookii.Dialogs.Wpf プロジェクト』をソリューションに追加して、プロジェクトのプロパティから『対象のフレームワーク』を fig. 4 のように作成中のバージョンに合わせます

fig.4 対象のフレームワーク

後は、InteractionRequest を呼び出しているプロジェクトへ Ookii.Dialogs.Wpf へのプロジェクト参照を追加すれば準備は完了です。

Ookii.Dialogs を使用する場合の最大の注意点は、『数多くの亜種が存在するが、今回作成した PopupCommonDialogAction を変更せず MVVM パターンで表示できるのは 1 種類のみ』と言う点で、上で紹介した GitHub 版 以外の Ookii.Dialogs を使用すると PopupCommonDialogAction に修正が必要になるので注意してください。

公式サイト で公開しているソースが .NET Framework 3.5 までの対応だったため有志が 4.0 以降の対応版を GitHub で公開した結果、何種類もの亜流が存在する状態になっていると予想しています。
Nuget からも入手できます(おそらく Nuget で公開しているのが正式継承版だと思われます)が Nuget から入手したバージョンだと PopupCommonDialogAction の変更が必要になります。

取得先として紹介した vsvyatski/Ookii.Dialogs GitHub 版 は CommonDialog から継承して作成しているため PopupCommonDialogAction を 1 か所も変更せず 3 種類のシステムファイルダイアログを開くことができるのでお勧めです。

Ookii.Dialogs のフォルダ選択ダイアログを表示する

フォルダ選択ダイアログを表示するのは非常に簡単で、MainWindow.xaml を src. 7 のように変更するだけです。

表示するだけであれば、Microsoft.Win32.OpenFileDialog を開いた時のコードのまま、src. 7 のハイライト行のみ修正するだけで fig. 5 のようにフォルダ選択ダイアログを表示できます。

fig.5 フォルダ選択ダイアログの表示

Ookii.Dialogs から表示できる 3 種類のコモンダイアログはプロパティ名を Microsoft.Win32.OpenFileDialog に合わせて作成されている為、とりあえず表示するだけなら OpenFileDialogConfirmation を渡せば表示できますが、VistaFolderBrowserDialog では選択したフォルダのパスを格納するための SelectedPath プロパティが必要です。

VistaFolderBrowserDialog 用の Confirmation も前章で紹介した FileSelectCommonDialogConfirmationBase を継承して不足しているプロパティのみ追加すれば良いので、GitHub リポジトリ に Up しているソースを直接見てください。

Ookii.Dialogs を使用してフォルダ選択ダイアログを表示する場合、上記の FolderSelectDialogConfirmation のみを作成するだけで fig. 5 のようにフォルダ選択ダイアログを表示できます。

Prism の InteractionRequest からコモンダイアログを表示するまとめ

前項で紹介した通り、Ookii.Dialogs の GitHub vsvyatski/Ookii.Dialogs 版 を使えば紹介したコードを変更せず 3 種のコモンダイアログを表示できるようになります。
そのため Microsoft.Win32 名前空間は不要になり、コモンダイアログの表示は Ookii.Dialogs に任せてしまえます。

既に Windows API Code Pack 導入済の場合、新規ライブラリへ変更するのは難しいかもしれませんが Ookii.Dialogs に変更する最大のメリットはファイルサイズです。Windows API Code Pack でコモンダイアログを表示する場合、【Microsoft.WindowsAPICodePack-Shell】と【Microsoft.WindowsAPICodePack-Core】の両方が必要なので DLL の合計サイズは約 700 KB ですが、Ookii.Dialogs は約 100 KB 程度なので Web やネットワーク経由で配布する場合、Windows API Code Pack の 1/7 のサイズで済むのは十分メリットがあると思います。

ただ、Windows API Code Pack はコモンダイアログ以外に多くの機能を含んでいるので、コモンダイアログ以外の機能が目当てで既に導入済みの場合、Ookii.Dialogs に変更する必要はないと思います。
※ その場合、今回紹介した PopupCommonDialogAction の変更は必須なので注意してください

Windows API Code Pack の詳細については『Windows API Code Pack連載 第1回~第10回の一覧 – 田中達彦のブログ』等で確認できます。
notice! 上記のサイトは画面キャプチャへのリンク切れが多いので見にくいかもしれません。

Ookii.Dialogs と Windows API Code Pack のどちらを導入するかは、プロジェクトの状況等で変わるので状況に合った方を選択すれば良いと思います。

WPF Prism episode シリーズ Season 1 あとがき

さて、WPF Prism episode シリーズはこの episode: 15 で一旦完結します。
episode: 3 ~ この episode: 15 までが理解できていれば、とりあえず簡単なアプリを作成できる程度には Prism を使いこなせるようになっているはずと思っています。

Prism の全機能を網羅してはいませんが、この連載で紹介した機能だけでも大体のアプリへ応用が利くと管理人的には考えています。WPF のコントロールやリソース、ビヘイビア等については Prism とは無関係で WPF 自体の機能なので個々で調べてもらうしかありません。

それに加えて管理人自身、手持ちの Prism ネタが尽きてしまったので連載記事として定期的に公開するのは一旦終了したい!と言うのが本音です。
まあ、ヘッダのタイトルに未練たらしく Season 1 とか付けているので書けそうなネタがあれば episode: 16、17 … として追加していく予定で、一応近い内に Season 1 のまとめ的なエントリを書いて一旦完結する予定です。

稚拙な文章を長々と読んで頂き本当にありがとうございました。
尚、今回紹介したコードも GitHub リポジトリ へアップしています。

 

 

2018/8/3 追記

Prism 7.2 が正式リリースされたので、Prism 7.2 の IDialogService についてのエントリを書きました!

 

【WPF Prism episode: 16 ~ Prism7.2、ダイアログは IDialogService でって言ったよね! ~】次回記事 →

 

 

あわせて読みたい

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

%d人のブロガーが「いいね」をつけました。