WPF Prism episode: 2 ~ WPF のフレームワーク決まってますか? 迷ってますか? Prism を選択してもらっていいですか? ~
episode: 1 では最終的に使用するフレームワークを決定しませんでした。
episode: 1 を書いていた頃は【MVVM light toolkit】を第一候補に考えていましたが、調べていくと MVVM light toolkit には MVVM パターンを実現するための最低限の機能は含まれているが、応用的な仕組みは使用者自身で作成する必要がある… 的な印象を受ける情報が多く、日本語での情報も最初に調べた頃からほとんど増えていない印象も受けたので、第一候補を【Prism】に変更して再度調べました。
Prism は最初からサポートしている機能が多いこと、日本語での情報も 1 年前よりは増えたような印象を持ったことで、最終的に使用する MVVM フレームワークは Prism に決定。
やはり、よく利用しそうな仕組みが最初から組み込まれていて楽できそう!と思ったことが 1 番の理由です。
2020/7/17 追記
WPF Prism episode シリーズ全体を整理して再構成した 2020 年度版の WPF 入門で、このエントリと同じ辺りの内容を紹介した .NET Core WPF Prism MVVM 入門 2020 Step: 3 を公開しました。
尚、この記事は Visual Studio 2017 Community Edition で .NET Framework 4.6以上 と C# を使用して 、WPF アプリケーションを MVVM パターンで作成するのが目的で、C# での基本的なコーディング知識を持っている人が対象です。
目次
Prism とは
Prism を使用して MVVM パターンのアプリケーションを作成すると、同じようなコードを何度も繰り返して書かざるを得ない苦行から解放されるのはもちろん、下 fig.1 のように多種の UI 部品を動的に読み込んだり切り替えたりするような画面の作成も簡単にできるようになります。
動的 UI 部品の読込機能等を標準機能のみで実現しようとした場合、コードビハインドへの記述が必要になったり、ビヘイビアが必要になる状況等も多く発生しそうですが、Prism を使用すると ViewModel への記述のみで解決できたり又、作成する各コンポーネント間がユニットテストを組み込みやすい疎結合を保った構成で作成できます。
動的 UI 部品読込機能を自分で作り込むことも可能でしょうが、その機能を備えたライブラリが既に存在していているのに、車輪の再発明をする必要はないでしょう。(業務等で使用する場合等は導入が難しいかもしれませんが、Microsoft 謹製だからと金の御旗を掲げて詰め寄ることができるかもしれませんw 事実、Prism 5.0 は現時点でも Microsoft からダウンロードできます。)
又、2017年12月から Xamarin でも WPF の作成が可能になり、Prism は Xamarin もサポートしているため、将来的に Xamarin を使ってみたいと思っているなら、覚えるメリットは十分にあると言えるでしょう。
※ 但し、Prism 7.1 は Xamarin.Forms.WPF に対応していないようで、対応するには Prism 自体のプロジェクトを修正して Nuget パッケージを自分で再作成する 必要があるようです。
(管理人は未確認 2019/3/4 追記)
Prism の基本的な用語
まずは、Prism の最も基本となる用語を紹介します。
Windows Form アプリケーションで例えると、スタートアップフォーム(プロジェクト)のようなものと考えることができます。
メインウィンドウやスタートアッププロジェクトを指してシェルと呼ぶ場合が多い。
公式サンプルに含まれる多くの例として、Shell 内に読込む View を含んだ DLL プロジェクトのことを指します。
Region に指定できるのは Prism 標準では ContentControl のみですが、他のコントロールを指定することもできます。
Prism の各種初期化を 1 か所で行うために使用します。
Prism 7.1 以降では Obsolete 属性が指定されて非推奨になりました。(2019/3/21 追記)
上記以外にも必要な用語はありますが、現時点では上記の 4 つだけ覚えば OK です。極端なことを言えば、上記以外の用語はほとんど使用しないと考えて良いと思います。必要があればその都度紹介していく予定です。
Prism の公式サンプル
以降は Prism の公式サンプルを使用しますが、あらかじめ Prism Template Pack インストールしておく必要はありません。各プロジェクトを開いた際に【Nuget パッケージの復元】でインストールされます。
あらかじめ、Prism Template Pack をインストールしておきたい場合は、episode: 1 を参照してください。
episode: 1 を書いていた頃は、公式サンプルの存在を知らなかったので、Prism でどんなことができるかを調べるのにかなり苦労しましたが、Prism の公式サンプル は 1 つ 1 つが非常に小さくシンプルで、Prism が提供する個々の機能を簡単に理解することができる優秀なサンプルが 29 種類も含まれています。
まずは、Prism の公式サンプル をリンク先の GitHub から取得してください。(GitHub からソースを取得する方法はここでは紹介しません)
以下が全サンプルの概要です。
1. | Bootstrapper and the Shell | Bootstrapper と Shell のサンプル。 |
2. | Regions | Region のサンプル。 |
3. | Custom Regions | StackPanel を Region として使用するためのアダプターを作成する。(最初から理解する必要はなし。必要に応じて参照すれば良い) Prism 標準で Region に指定できるのは ContentControl のみですが、他のコントロール(ここでは StackPanel)を Region に指定したい場合の方法です。 |
4. | View Discovery | View Discovery によって View を自動的に注入する。(View を Shell のコードビハインドから注入する。コードビハインドに何も記述したくない場合はとりあえず見なくても OK) |
5. | View Injection | View Injection を使って手動で View を動的に追加する。(ボタンの Click イベントでロードするサンプル。イベントハンドラはコードビハインドに記述) |
6. | View Activation/Deactivation | View を動的にアクティベートしたりディアクティベートする。(コードビハインドに記述したイベントハンドラから実行) |
7. | Modules – AppConfig | App.config ファイルを使用して Module をロードする。 |
7. | Modules – Code | ロードする Module をソースコードから指定する。 |
7. | Modules – Directory | ディレクトリ配下の Module を自動的にロードする。 |
7. | Modules – LoadManual | IModuleManager を使って手動で Module をロードする。(コードビハインドに記述したイベントハンドラから実行) |
8. | ViewModelLocator | ViewModelLocator を使用した View と ViewModel の関連付け。 |
9. | ViewModelLocator – Change Convention | ViewModelLocator のネーミング規約を変更する。 |
10. | ViewModelLocator – Custom Registrations | View と VewiModel の組み合わせを手動で登録する。 |
11. | UsingDelegateCommands | DelegateCommand と DelegateCommand<T> を使う。 |
12. | UsingCompositeCommands | コンポジットコマンドを使って複数のコマンドを一つのコマンドとして呼び出す。(「全てを保存」ボタン) |
13. | IActiveAware Commands | アクティブな場合にのみコマンド(CompositeCommands)を実行する。 |
14. | UsingEventAggregator | IEventAggregator を利用して ViewModel – ViewModel 間でデータをやり取りする。 |
15. | EventAggregator – FilteringEvents | EventAggregator に登録する際、受け取るイベントを絞り込む。 |
16. | RegionContext | Module 内の View に Region を定義して View を切替える。(Shell だけでなく Module 内の View にも Region の定義が可能) |
17. | BasicRegion Navigation | 基本的な Region ナビゲーション(画面遷移)。 |
18. | Navigation Callback | ナビゲーションが完了した時に通知を受け取る。 |
19. | Navigation Participation | INavigationAware を使って View 切替え前後に動作を設定する。 |
20. | Navigate to existing Views | View 切替え時の View の生存期間をコントロールする。(同じ View を再利用するか新規に作成するか) |
21. | Passing Parameters | View/ViewModel から別の View/ViewModel にパラメータを渡す。 |
22. | Confirm/cancel Navigation | IConfirmNavigationReqest インターフェイスを使って「確認」「取消」のメッセージボックスを表示する。 |
23. | Controllng View lifetime | IRegionMemberLifetime を使ってメモリーから View を自動的に取り除く。 |
24. | Navigation Journal | 同一 Region 内の View 切替え履歴を管理する。(「戻る」「進む」ボタン) |
25. | Interactivity – NotificationRequest | InteractionRequest を使ってメッセージ画面を表示する。(MessageBox クラスは不使用) |
26. | Interactivity – ConfirmationRequest | InteractionRequest を使って確認用ダイアログを表示する。(「OK」「Cancel」ボタン) |
27. | Interactivity – Custom Content | InteractionRequest でダイアログに独自に作成した View を設定する。 |
28. | Interactivity – Custom Request | InteractionRequest でポップアップした View と値をやり取りする。 |
29. | Interactivity – InvokeCommandAction | コントロールのイベントにコマンドをバインドする。 |
episode:3 以降では公式サンプルで紹介された機能を使って簡単なサンプルアプリを作成しながら Prism の使い方を紹介しています。(2019/2/25 追記)
Prism 公式サンプルを実行するために必要な手順
まずは、ダウンロードした公式サンプル内の『14-UsingEventAggregator』直下にあるソリューションを開きます。
Visual Studio が起動したら、ソリューションエクスプローラーのソリューションを右クリックして、[NuGet パッケージの復元]又は、[ソリューションの NuGet パッケージの管理]をクリックすると NuGet から必要なコンポーネントが取得されます。
後はビルドして実行するとメインウィンドウ(シェル)が表示されます。
ここで説明した『14-UsingEventAggregator』以外のプロジェクトでも上記の方法を実行するとエラーを解消すことができます。
Prism 公式サンプル『14-UsingEventAggregator』のソリューション構成
- ModuleA プロジェクト
- Shell の左側に読み込まれる View を含む Module。
- ModuleB プロジェクト
- Shell の右側に読み込まれる View を含む Module。
- UsingEventAggregator プロジェクト
- このソリューションのShell。
- UsingEventAggregator.Core プロジェクト
- EventAggregator で受け渡しするクラス定義を含むプロジェクト。
Prism の Shell とエントリポイント(Prism 6.3 まで)
アプリケーションのエントリポイントとなる、App.xaml.cs です。(自動生成されます)
1 2 3 4 5 6 7 8 9 10 11 12 13 | /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var bootstrapper = new Bootstrapper(); bootstrapper.Run(); } } |
Application クラスの OnStartup イベント内で生成した Bootstrapper が Prism アプリケーションの起点になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | internal class Bootstrapper : UnityBootstrapper { protected override DependencyObject CreateShell() { return Container.Resolve<MainWindow>(); } protected override void InitializeShell() { Application.Current.MainWindow.Show(); } protected override void ConfigureModuleCatalog() { var catalog = (ModuleCatalog)ModuleCatalog; catalog.AddModule(typeof(ModuleAModule)); catalog.AddModule(typeof(ModuleBModule)); } } |
この Bootstrapper クラスで Prism の各種初期化処理を実装します。
1 ~ 11 行目までは Prism Template で自動生成された部分です。
13 ~ 18 行目のオーバーライドしたメソッドでアプリケーションに読み込むモジュールを定義しています。
このようにアプリケーション内で使用したいモジュールは Bootstrapper 内で定義することで読み込まれますが、ソースコードへ記述する方法だけでなく、App.config 内に定義したり、指定したディレクトリ内を検索して見つかったモジュールを自動で登録することもできます。
実際の運用を考えると、ディレクトリ内に配置して自動登録する方法が最も適していると管理人は思いますが、ここでは説明しません。
ディレクトリ内の Module を読み込んで自動的に登録したい場合は、Prism 公式サンプルの『07-Modules – Directory』にあるので必要な場合は個別に参照してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <Window x:Class="UsingEventAggregator.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" Title="{Binding Title}" Height="350" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ContentControl prism:RegionManager.RegionName="LeftRegion" /> <ContentControl Grid.Column="1" prism:RegionManager.RegionName="RightRegion" /> </Grid> </Window> |
4 行目は XAML で Prism を使用するための XML 名前空間のエイリアスです。
5 行目は、View と VM の関連付けに ViewModelLocator を使用するかを設定しています。(Trueに設定すると、View の名前が Views.MainWindow の場合 DataContext に設定する VM は ViewModels.MainWindowViewModel になります)
View と VM の関連付けを変更したい場合は、公式サンプル『9. ViewModelLocator – Change Convention』又は、『10. ViewModelLocator – Custom Registrations』を参考にすると変更することができます。
(2019/3/21 追記)
11 行目と 12 行目は Module を読み込むためのリージョン名の定義です。
ここで定義したリージョン名は Module を作成する場合にも使用します。
以上が Shell の設定で、引き続いて Shell に読み込む Module の設定方法を紹介します。
Prism Module の初期化時に指定する内容
サンプル内には Module が 2 つありますが、基本的な内容は同じため、ModuleA のみを例に挙げます。
Module には必ず IModule インタフェースを継承したクラスを含める必要があり、Module 自身の初期化や Shell に配置した Region との関連付け等を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ModuleAModule : IModule { IRegionManager _regionManager; public ModuleAModule(RegionManager regionManager) { _regionManager = regionManager; } public void Initialize() { _regionManager.RegisterViewWithRegion("LeftRegion", typeof(MessageView)); } } |
10 ~ 13 行目で View を表示したい Shell 側に設定した Region を指定しています。
Prism を使用した View の動的読込は、上で紹介したソースを書くだけで後は全て Prism にお任せで動いてくれます。予想に反して少ないコード量だったのではないでしょうか。
※ サンプルには EventAggregator を使用した ViewModel – ViewModel 間の通信も含まれていますが、今回は触れません。
紹介したサンプルが No.1 からではなく、飛ばして No.14 を紹介したのは、UI 部品の動的読込についてはこのサンプルが全てだからです。No.14 が理解できていれば、No.14 以前のサンプルはそれぞれの機能が単機能単位に書かれているだけなので、特に悩むことなく理解できると思っています。
Prism で UI 部品の動的読込を行うために最低限必要なことは以上ですが、今回の記事はここまでとして、次回は実際にアプリケーションを作成する記事になる予定です。
2020/7/17 追記
WPF Prism episode シリーズ全体を整理して再構成した 2020 年度版の WPF 入門で、このエントリと同じ辺りの内容を紹介した .NET Core WPF Prism MVVM 入門 2020 Step: 3 を公開しました。
次回記事「Re: ゼロから始める Prism 生活【episode: 3 WPF Prism】」