Prism はじめました【step: 3 .NET Core WPF Prism MVVM 入門 2020】
前回は MVVM パターンの概要と WPF 用の MVVM フレームワークをいくつか紹介して、その中からこの連載で使用するフレームワークに Prism を選択しました。
今回は Prism の概要とインストール手順及び、Prism を使用した WPF アプリの作成手順等を紹介します。
尚、この記事は Visual Studio 2019 Community Edition で .NET Core 3.1 以上 と C# に加えて Prism を使用して、WPF アプリケーションを MVVM パターンで作成するのが目的なので、C# の文法や基本的なコーディング知識を持っている人が対象です。
目次
MVVM フレームワーク Prism
Prism は元々 Microsoft Patterns and Practices で『WPF 及び Silverlight で「複合アプリケーション」を構築するための公式ガイダンス』を意味するコード名でしたが、現在ではそれがそのままフレームワーク名になり、Brian Lagunas さんを始めとする MVP(Most Valuable Professional)の方々を中心に GitHub でメンテナンスされています。
Prism は大まかに以下のような特徴を持った MVVM フレームワークです。
- Module と呼ばれる独立した DLL プロジェクトに含めた UserControl を動的に Load して Window へ配置するための仕組みを備えた複合アプリケーションの作成。
- WPF、Xamarin.Forms 等複数の XAML プラットフォームへの対応。
※ 次期リリース予定の Ver. 8 では UNO Platform への対応が追加予定。
※ UWP 単体のサポートは削除され、Xamarin.Forms から生成される UWP のみに対応
※ Xamarin.Forms の WPF には未対応。 - .NET Framewok 4.5 及び .NET Core 3.0 以降のサポート
- 簡単に新規プロジェクトや新規オブジェクトを追加できるテンプレートパックの提供
- 疎結合な関係で作成された部品間でデータをやり取りしたり、テストをし易くするための DI コンテナの提供
Prism は MVVM フレームワークと言うより、複合アプリケーションを作成するためのフレームワークと言うイメージが強いので、多人数が参加する大規模システム向けのフレームワークだと紹介される事も多い気もしますが、管理人的には小規模アプリから大規模システムまでカバーできる汎用的で優れたフレームワークだと思っています。
Prism を使用した場合のメリット
前回紹介したものも含め、数多くの MVVM フレームワークの中から Prism を選択したのは以下のようなメリットがありそうと考えたからです。(管理人個人の感想です)
- 日本語情報も多いので困った場合に解答が見つかり易そう
- 何となく将来的にも長く使用できそう
- 画面部品を DLL に分割した複合アプリケーションが作成できる
- MVVM パターンで WPF アプリを作成するための実装クラスが含まれているので、自分で実装する必要が無い
- 新規プロジェクトや新規 Window、UserControl 等を追加する場合のテンプレートも公開されている
- UNO Platform のサポートも予定されている
本来、フレームワークのメリットは複数のフレームワークを使って実際にアプリを作成した結果を元に比較すべきでしょうが、前回も書いた通り管理人は Prism 以外のフレームワークをまともに使用したことがありません。そのため、上で紹介したメリットは Prism 以外のフレームワークにも当てはまる場合があります。
管理人が考える Prism 最大のメリットは【日本語情報の手に入り易さ】です。一応、この連載も含め以前連載していた WPF Prism episode シリーズもあるので、日本語の Prism 情報量だけならどこにも負けていない自負はありますし、個人ブログや Qiita で Prism の記事を見かける事も多いので困った場合の解決法も見つかり易いと思っています。
加えて【将来に渡ってのリリースが期待できそう】と言うのも Prism を選択した場合の大きなメリットだと思っています。今後何年先まで… のような保証が無いのは確かですが、Step: 1 でも紹介した次世代 UI プラットフォームの MAUI サポートも既に Issue が上がっている ので対応してくれそうです。
ただ… 管理人的には『WPF アプリの作成に MVVM フレームワークの使用を決定した』事自体が既に最大のメリットだと考えているので、ぶっちゃけどのフレームワークを選んでも機能的には大差ないと思っています。
とは言え、この連載では Prism を使って WPF アプリを作成する方法を紹介するのが目的なので、次章では Prism を使用するための前準備を紹介します。
Prism を使用するための前準備
Visual Studio 2019 を起動して新規プロジェクトを作成する前準備として Prism Template Pack をインストールします。無くても WPF 用の標準プロジェクトテンプレートから手作業で作成する事もおそらく可能だとは思いますが、管理人はやってみようと思ったことも無いのでこの連載は Prism Template Pack がインストール済みである事を前提で進めます。
Prism Template Pack のインストール
Prism Template Pack をインストールするには、Visual Studio を起動して [拡張機能] – [拡張機能の管理] を選択して【拡張機能の管理ダイアログ】を表示します。ダイアログの左側が『Visual Studio Marketplace』になっているのを確認して、fig. 1 のように検索ボックスへ『prism』を入力すると Prism Template Pack が検索されます。
検索結果の Prism Template Pack を選択すると右上辺りに『ダウンロード ボタン』が表示されるので、Click してインストールします。(管理人の環境には既にインストール済みなのでボタンが表示されていません)
Prism Template Pack インストール後の設定変更
本項の内容は必須ではありませんが、仕事等で Prism を使用する場合は設定しておいた方が良いと思います。上と同じく [拡張機能] – [拡張機能の管理] から【拡張機能の管理ダイアログ】を表示して、fig. 2 のように左側の【インストール済み】を選択して、Prism Template Pack を選択します。
Prism Template Pack を選択して表示される右側の【この拡張機能を自動的に更新する】のチェックを外すと Prism Template Pack はその時点のバージョンで固定されます。
Prism のテンプレートには Prism のバージョンが指定されているため、Prism Template Pack が更新されると Nuget から取得する Prism のバージョンが変わってしまう場合があるのを防ぐことができます。個人で開発している場合はそこまで気にする必要も無いと思いますが、仕事の場合、使用する Prism のバージョンは決まっているはずなので自動更新は外しておいた方が良いと思います。
Prism 公式サンプル
次章からは実際に Prism を使用して WPF アプリを作成する方法を紹介しますが、アプリの作成を開始する前に Prism がサポートしている機能を押さえておいた方が理解も早まると思うので、Prism の WPF 公式サンプル を取得する事をお勧めします。
上記のサンプルは Prism がサポートしている機能ごとの小さいプロジェクトに分割されているので、それぞれのコードを追うだけで Prism の機能は大体理解できるようになっている優れたサンプルです。上記サンプルの詳細は WPF Prism episode: 2 を見てください。
管理人が以前連載していた WPF Prism episode シリーズが書けたのもこの公式サンプルに出会えたおかげだと言って良いくらいの分かり易いサンプルなのでぜひ取得する事をお勧めします。この連載は公式サンプルを全て網羅する予定ではないので、この連載で取り上げない機能は公式サンプルを確認してください。
次章からは実際に WPF アプリを作成する手順を紹介します。
Prism のプロジェクトテンプレートから新規プロジェクトを作成
以降は Prism Template Pack がインストールされている事が前提です。まずはスタートアッププロジェクトとして実行可能プロジェクトを作成するために、fig. 3 の Visual Studio のスタートウィンドウで【新しいプロジェクトの作成】を選択します。
次画面のフィルター項目を以下のように設定します。
- 言語:C#
- プラットフォーム:Windows
- プロジェクトの種類:デスクトップ
そして検索ボックスに『prism』を入力すると Prism Template Pack に含まれるプロジェクトテンプレートが表示されるので『Prism Blank App (.NET Core)』を選択して次ページへ進みます。
Select Container ダイアログ
新しいプロジェクトの構成入力画面は通常のプロジェクトと同じように保存場所などを指定しますが、Prism のテンプレートを選択した場合、作成ボタン Click 後に fig. 4 の【Select Container ダイアログ】が表示され、DI コンテナを選択する必要があります。
fig. 4 の Select Container ダイアログから『dryIoc』、『Unity』の内いずれか 1 つを選択する必要がありますが、初めての場合はどちらを選択して良いか分からないと思います。(管理人も当時は分かりませんでした)
Prism 組み込みの DI コンテナについては次回エントリで詳しく紹介する予定なのでここでの紹介は省きますが、どちらを選択して良いか分からない場合はとりあえず【Unity】を選択すれば良いと思います。ぶっちゃけ普通に WPF アプリを作成するだけならどちらを選択しても大きな違いはありません。
DI コンテナを選択したら【CREATE PROJECT ボタン】を Click して次へ進んでください。前画面に戻りたい場合は fig. 4 のように Select Container ダイアログの [×] ボタンClick で戻る事ができます。
Prism Blank App (.NET Core) テンプレートから生成されるプロジェクト
Prism Blank App (.NET Core) を選択した場合は fig. 5 のようなプロジェクトが生成されます。
生成されたプロジェクトはすぐ実行できるようになっていますが、実行前にプロジェクトのプロパティを開いて【対象のフレームワーク】を確認してください。Prism Template Pack Ver.2.2.1 でプロジェクトを作成すると対象のフレームワークが .NET Core 3.0 で作成されます。.NET Core 3.1 で作成する場合は fig. 6 のように変更してから実行してください。
※ 対象のフレームワークに .NET Core 3.1 が表示されない場合は SDK をインストール してください。
対象のフレームワークを確認後に実行すると fig. 7 のようなコントロールを何も配置していない空の Window が表示されます。
Prism Template Pack から生成される MainWindow の Title プロパティは MainWindowViewModel の Title プロパティとバインドするように生成されるので、Title に『Prism Application』が表示されていればデータバインディングも正常に動作しています。
Prism Full App (.NET Core) テンプレート
最新の Prism Template Pack には新たに『Prism Full App (.NET Core)』テンプレートも追加された(いつのバージョンから追加されたか確認できませんでした)ようで、選択すると fig. 8 のような Module プロジェクト、Service プロジェクトとテストプロジェクトまで含んだソリューションが作成されます。
Prism が想定しているシナリオが形になったようなソリューションで、Service を DI コンテナと組み合わせて使用する機能まで組み込まれたサンプル感の高いテンプレートだと思いますが、有用かどうかは人それぞれのような気がします。管理人はおそらくこのテンプレートを使用する事は無さそうですが、xUnit を使用した VM のテスト内容等も確認できるためサンプルとしては有用な気がします。
尚、このテンプレートでは対象のフレームワークが【.NET Core 3.1】のプロジェクトが作成されるようです。
次章では Prism Blank App (.NET Core) テンプレートから作成したプロジェクトの起動処理について紹介します。
PrismApplication
Prism 6 系まではアプリ起動時の処理を Bootstrapper に記述していたため未だに『Bootstrapper』や『UnityBootstrapper』等の検索ワードでこのサイトに来られる方もいるようですが、Prism 7 以降では起動時処理を Application クラスを継承した PrismApplication に記述するように変わりました。
Prism 8 からは Bootstrapper が復活するような Issue も上がっているので、PrismApplication は Prism 7 だけになるかもしれません(詳しくは調べていません)が、現時点(2020/6 現在)の Prism Stable 版では PrismApplication へ起動時の処理を記述します。
PrismApplication に処理を記述すると言う事は App.xaml のコードビハインドへ記述すると言う意味なので、App.xaml.cs を開いてください。
PrismApplication でオーバーライド可能なメソッド
PrismApplication には以下のオーバーライド可能なメソッドが定義されています。
メソッド名 | 内容 |
---|---|
CreateShell | アプリでメインに使用する Window(Prism では Shell と呼ばれる)を作成する(new する)メソッド。 テンプレートから生成された App.xaml にデフォルトでオーバーライドされているメソッドで、プロジェクト生成直後は MainWindow を new して返すオーバーライドが記述されています。 Application.StartupUri に MainWindow を設定するのと同じ意味になります。 MainWindow のインスタンス生成時に別途プロパティ等を設定したい場合以外はそのままで使用する事の多いメソッド。 Shell のクラス名を MainWindow から変更する場合は変更が必要です。 |
RegisterTypes | DI コンテナに登録する型を指定するメソッド。 テンプレートから生成された App.xaml にデフォルトでオーバーライドされているメソッド。(内部の実装は無し) |
ConfigureModuleCatalog | Module(動的に Load するための UserControl を含んだ DLL プロジェクトを指す)を Prism に登録するためのメソッド。 |
ConfigureViewModelLocator | View と ViewModel を関連付ける命名規則を指定するメソッド。 |
CreateModuleCatalog | 指定したディレクトリ内の全 Module を Prism へ登録するメソッド。 |
ConfigureRegionAdapterMappings | 自作した RegionAdapter(Module に置いた UserControl を配置できるコンテナコントロール)を Prism に登録するためのメソッド。 |
ConfigureServiceLocator | 詳細不明。どのように使用するのか調べても分かりませんでした。 |
ConfigureDefaultRegionBehaviors | RegionBehavior を継承して自作した RegionBehavior を Prism に登録するためのメソッド。 |
CreateContainerExtension | Prism がサポートしていない DI コンテナを Prism に登録するためのメソッド。 |
InitializeModules | Module を初期化するメソッド。 |
InitializeShell | Shell を初期化するメソッド。 |
OnInitialized | Prism 初期化完了後に呼び出されるメソッド。 |
OnStartup | Application.Startup イベントを発生させます。 |
RegisterFrameworkExceptionTypes | Prism が例外として扱わない例外を登録するメソッド。 |
RegisterRequiredTypes | 詳細不明。 基本的には RegisterTypes と同じく DI コンテナへ型を登録するためのメソッドだと思いますが、RegisterTypes との違いがよく分かりませんでした。 |
PrismApplication でオーバーライド可能な全メソッドを紹介しましたが、内容に書いている用語が何を指すのか分からない事も多いと思います。この時点ではオーバーライドしたら何が設定できるメソッドなのか分からなくても特に問題はありません。
実際にオーバーライドする事が多いのは上から 3 ~ 4 行目までのメソッドで、それ以外のメソッドをオーバーライドする機会はあまり多くないと思いますし、現時点ではほとんど分からなくても問題ありません。実際にオーバーライドが必要なメソッドはその都度紹介します。
Prism 初期化メソッドの実行順序
PrismApplication でオーバーライド可能なメソッドを全て理解する必要はありませんが、メソッドの実行順序だけは紹介します。fig. 9 が全メソッドをオーバーライドして Debug.WriteLine で出力された実行順です。
先頭の PrismApplication_Startup は Application の Startup イベントハンドラで一番最初に実行されます。2 番以降のブルーで塗りつぶしたメソッドが上で紹介した PrismApplication でオーバーライド可能なメソッドです。オーバーライド可能なメソッドは fig. 9 の順に呼び出されるため、Prism の全機能は 15 番目に呼び出される OnInitialized メソッドで初期化が完了します。
一方、Application.Startup イベント内では Prism の初期化も始まっていないため、Prism が提供する各種クラスは使用できませんが、アプリの二重起動防止のような Prism の機能を使用しないような処理であれば Application.Startup イベントで処理できます。
Application.Startup イベント
Windows Form ではアプリの二重起動防止に、エントリポイントに指定した Main メソッド内で処理する事も多かったと思います。WPF でもエントリポイントに指定した Main メソッド内で処理する事もできますが、二重起動を防ぎたいだけなら Application.Startup イベントでも可能です。
Application のイベントへ処理を追加するには fig. 5 の一番下に見える App.xaml へ fig. 10 のように入力すれば App.xaml のコードビハインド(App.xaml.cs)へイベントハンドラが作成されます。
イベントは XAML に直接入力する以外に、fig. 11 のようにプロパティウィンドウから追加することもできます。
fig. 11 のように追加したいイベント名右側の TextBox をダブルクリックするとイベントハンドラが追加されます。(ここでは Exit イベントを追加)
追加された Application のイベントハンドラへ src. 1 の処理を追加すると二重起動を防止できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | using System.Threading; using System.Windows; using Prism.Ioc; using PrismSample.Views; namespace PrismSample { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } private Mutex mutex = new Mutex(false, "HalationGhostPrismSample"); /// <summary>ApplicationのStartupイベントハンドラ。</summary> /// <param name="sender">イベントのソース。</param> /// <param name="e">イベントデータを格納しているStartupEventArgs。</param> private void PrismApplication_Startup(object sender, StartupEventArgs e) { if (this.mutex.WaitOne(0, false)) return; MessageBox.Show("二重起動できません。", "情報", MessageBoxButton.OK, MessageBoxImage.Information); this.mutex.Close(); this.mutex = null; this.Shutdown(); } /// <summary>ApplicationのExitイベントハンドラ。</summary> /// <param name="sender">イベントのソース。</param> /// <param name="e">イベントデータを格納しているExitEventArgs。</param> private void PrismApplication_Exit(object sender, ExitEventArgs e) { if (this.mutex != null) { this.mutex.ReleaseMutex(); this.mutex.Close(); } } } } |
アプリ起動時に Mutex をチェックして、既に起動済みの場合はメッセージを表示してアプリを終了しています。Mutex 生成時(new する時)に指定する文字列は一意になるような名前を指定する必要があるので注意してください。
そして、アプリ終了時に Mutex を解放する事も必要です。src. 1 の多重起動防止は Prism で作成した場合の方法と言う訳ではなく WPF アプリであればほとんどの場合に有効な方法です。
src. 1 では Startup イベントと Exit イベントだけを処理しています。一般的なアプリの場合は DispatcherUnhandledException イベントで未処理の例外への対応等も追加すべきだと思いますが、ここでは紹介しません。又、別の機会があれば紹介したいと思います。
Prism の Shell と Module
Prism では実行可能プロジェクト(一般的にはスタートアッププロジェクト)に含まれるアプリ内で中心的に使用するための Window を Shell と呼び、複合アプリケーションの部品として動作する クラスライブラリプロジェクトを Module と呼びます。そして Prism は Module に含まれる UserControl を【部分 View】として Shell 上に動的 Load する事ができます。
部分 View は Shell 全体を覆うように 1 つだけ配置することもできますし、部分 View を同一画面内に複数の同時に配置することもできます。このように画面部品を別プロジェクトに分離して開発できるため大規模システム向きと言えるかもしれませんが、部分 View は Shell と同じプロジェクトに配置することもできるので Prism は小規模アプリでも有用なフレームワークだと思います。
サンプルとして紹介するには単一のプロジェクトで完結する方が良いのかもしれませんが、Prism の特徴である複合アプリケーションは Module と組み合わせて作成する場合が多いので、まずは上で紹介したソリューションに Module を追加する手順を紹介します。
ソリューションに Module を追加
まずは通常のプロジェクトをソリューションに追加する場合と同じように fig. 12 の『新しいプロジェクトを追加ダイアログ』を表示します。
fig. 12 のように【Prism Module (.NET Core)】を選択して追加するとソリューションエクスプローラーは fig. 13 のようになります。
大したことはない注意点ですが、Module のプロジェクト名を『~ Module』のように『Module』で終わる名前にしてしまうと、Module プロジェクト内に自動生成される Module クラスの名前が『TestModuleModule』のようになってしまう(fig. 13 では ModuleSampleModule)ので気を付けた方が良いと思います。名前は後から変更できるので注意と言う程ではありませんが、不要な手間なら省いた方が良いと思います。
Module を Prism に登録
Module を使用するには Prism に登録する必要があり、以下の 2 通りの方法から選択します。
- Shell のプロジェクトに Module への参照を追加して登録する
- 指定したフォルダ内の Module を全て動的に登録(参照は追加しない)
2 通りの内どちらか一方を選択することもできますし、両方を併用することもできますが、Module ごとにどちらの方法で登録するかを選択する必要があります。この連載では『Shell のプロジェクトに Module への参照を追加して登録する』方法を紹介します。
尚、連載内では『フォルダ内の Module を動的に登録する方法』を紹介しない予定なので Prism 公式 Sample の【07-Modules – Directory】の App.xaml.cs を見てください。
Module を Prism に登録するにはまず、PrismSample プロジェクトに ModuleSample プロジェクト(Module)の参照を追加して、src. 2 のように PrismApplication.ConfigureModuleCatalog をオーバーライドします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System.Diagnostics; using System.Threading; using System.Windows; using Prism.Ioc; using Prism.Modularity; using Prism.Regions; using PrismSample.Views; namespace PrismSample { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } /// <summary>Moduleカタログを設定します。</summary> /// <param name="moduleCatalog">設定するModuleカタログを表すIModuleCatalog。</param> protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { base.ConfigureModuleCatalog(moduleCatalog); moduleCatalog.AddModule<ModuleSampleModule>(); } ~ 略 ~ } } |
ConfigureModuleCatalog メソッド内で IModuleCatalog へ Module を追加すると Prism に登録できます。Module を新規追加する度に ConfigureModuleCatalog へ IModuleCatalog.AddModule を追加する必要があり、追加し忘れた Module は Prism から認識されません。この段階では Module へ何も記述していないので、Prism は何もしない Module と認識していて、実行しても fig. 7 と同じ画面が表示されます。
src. 2 の 29 行目にベースメソッドの呼び出しを残していますが、ベースメソッドは何も実装されていないので削除しても構いません。(2020/8/18 takker さんの指摘で追記)
Module を追加した場合は ConfigureModuleCatalog への追加も必須になる事は覚えておいてください。
※ フォルダ内の Module を自動登録する場合は不要です
View と VM を関連付ける命名規則を変更
フレームワークを使用せず WPF の標準機能のみで作成する場合、View と VM をデータバインディングで接続するには src. 3 のように View の DataContext プロパティへ VM を手動で設定していました。(コードビハインドに記述することもできます)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <Window x:Class="PlaneWpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:PlaneWpfApp" mc:Ignorable="d" Title="MainWindow" Height="480" Width="640" WindowStartupLocation="CenterScreen"> <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> ~ 略 ~ </Window> |
Prism Template Pack から生成した Window や UserControl の宣言部には src. 4 のように ViewModelLocator.AutoWireViewModel が自動的に追加されます。(src. 4 は MainWindow)
1 2 3 4 5 6 7 8 9 10 | <Window x:Class="PrismSample.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> <ContentControl prism:RegionManager.RegionName="ContentRegion" /> </Grid> </Window> |
5 行目の ViewModelLocator.AutoWireViewModel に True が設定されている場合、Prism はデフォルトの命名規則に従って VM を自動的に探し出して DataContext に設定してくれます。デフォルトの命名規則は
【View:Views.Hoge の場合、VM:ViewModels.HogeViewModel】
になっています。
ViewModelLocator.AutoWireViewModel に False を設定するか、記述を削除すると VM を手動で設定することもできますが、src. 3 のように XAML に設定するのではなく必ずコードビハインドから設定する必要があります。DI コンテナが関係するので詳しくは次回紹介します。
Prism では View と VM は自動で関連付けされることが前提ですが、関連付けの命名規則を変更したい場合は変更することもできます。
管理人は『View や ViewModel』のようなアーキテクチャに関連する名前はプロジェクト名に適用して、名前空間は『機能名』を元に論理的なグループに分けるのが好みです。そのため、View と VM は同じ名前空間に配置したいので、Prism 標準の命名規則は必ず変更しています。
View と VM を同じ名前空間に配置したい場合、src. 5 のように PrismApplication.ConfigureViewModelLocator をオーバーライドすると Prism の命名規則を変更できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | using System; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Windows; using Prism.Ioc; using Prism.Modularity; using Prism.Mvvm; using Prism.Regions; using PrismSample.Views; namespace PrismSample { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } ~ 略 ~ /// <summary>ViewModelLocatorを設定します。</summary> protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(vt => { var viewName = vt.FullName; var asmName = vt.GetTypeInfo().Assembly.FullName; var vmName = $"{viewName}ViewModel, {asmName}"; return Type.GetType(vmName); }); } ~ 略 ~ } } |
src. 5 の通りに記述すると、View と同一名前空間にある【View 名 + ViewModel】と言う名前のクラスを DataContext へ設定してくれるようになります。尚、関連付け命名規則の変更は PrismApplication に 1 か所記述するだけで、結合される全 Module にも同じ命名規則が適用されます。
余談ですが、View と VM の関連付け命名規則を変更すると fig. 12 のソリューション構成も変更する必要があります。その際、MainWindow はドラッグ & ドロップで場所を変更してもソースコードの修正が必要な箇所は大して多くありませんが、Module の場合は部分 View(UserControl)名の変更も必要なはず(ViewA のような名前のまま使用しないと思います)なのでソースコードの変更箇所も多くなります。
Module の場合、部分 View の場所をドラッグ & ドロップで変更するのではなく、新規で部分 View を追加して元の部分 View は削除する方が手間が少ないと思います。Prism Template Pack には UserControl のテンプレートも含まれているので、fig. 14 の『新しい項目の追加 ダイアログ』から選択するだけで追加できます。
但し、fig. 14 の『新しい項目の追加 ダイアログ』から追加した UserControl は src. 6 の XAML デザイナ関連の xmlns が設定されないので、ViewA を削除する前に ViewA からコピペしておくと手間が省けると思います。
1 2 3 4 | xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" |
Prism の View・VM 関連付け命名規則がデフォルトの場合、UserControl や Window を Views 名前空間に新規追加すると VM も ViewModels 名前空間へ自動で追加されますが、命名規則を変更した場合、VM は自動追加されないため、手動で追加する必要があるので注意してください。
構成変更後のソリューションは fig. 15 のようになります。
※ Shell のプロジェクトと Module のルート名前空間は同一にしています。
現時点ではルート名前空間しか定義していないため、fig. 13 と比べるとかなりシンプルな構成になったと思いますが、シンプルな構成にする事が目的ではなく機能別の名前空間にする事が目的です。
ConfigureViewModelLocator のオーバーライドは必須ではありませんし、名前空間も好みの問題だと思うので、View や VM を配置する名前空間を変更したいと思った時に試してみてください。
その他のよくオーバーライドするメソッド
PrismApplication でオーバーライド可能なメソッドの内、テンプレートから作成した場合にデフォルトでオーバーライドされている CreateShell、RegisterTypes の 2 つを紹介していませんが、CreateShell は一覧の内容に書いた通りデフォルトから変更する機会が管理人的にはあまり無いのでこれ以上紹介する事がありません。
残りの RegisterTypes メソッドは先に DI コンテナについての紹介が必要だと思っているので、次回エントリに持ち越します。
デザインフレームワークのススメ
ここまでで Prism で作成するアプリの起動時に設定可能な情報は紹介できたと思います。この時点で実行すると fig. 16 の Window(Prism では Shell)が表示されます。
fig. 16 は fig. 7 をそのまま流用しましたが、管理人的にはかなり地味な画面だと思います。派手な画面が良い!等と思っている訳ではありませんが『グラフィカルな画面を自由に作成できる』事が売り文句の WPF なのに管理人的に fig. 16 は『グラフィカルな画面』だとは思えません。
事実、WPF では Windows Form で変更できなかった箇所が変更できるようになっていますが、アプリで使用する全コントロールに統一的なデザインを適用していくのはかなりハードルが高い面倒な作業だと思います。
デザインセンスがあってデザインが得意な人なら問題ないでしょうが、そんな人がこの記事を読むような事はあまり無さそうだと思っているので、インストールするだけで標準のコントロールに統一的なデザインを適用できるデザインフレームワークもついでに紹介します。
デザインフレームワークの使用
WPF でデザインフレームワークと言うと必ず名前が挙がる MahApps.Metro と Material Design In XAML Toolkit の 2 つをインストールして数か所に設定を追加するだけで、fig. 17 のような画面になります。
fig. 17 のデザインが『最高にイケてる!』とか『使わないと損!』等と絶賛するつもりはありませんが、標準とは少し違ったモダンっぽい画面の方が作る方も楽しいと思います。少なくとも管理人はその方が楽しいです。
デザインフレームワークは WPF アプリに必須ではありませんが、MahApps.Metro と Material Design In XAML Toolkit は Nuget からインストールするだけで手軽に画面の雰囲気を変える事ができますし、現在(2020/7 現在)このサイトで別途連載中の WPF UI Gallery シリーズがあるので連動企画として、この WPF 入門でも MahApps.Metro と Material Design In XAML Toolkit をインストールした状態のサンプルアプリを紹介しようと考えています。
MahApps.Metro と Material Design In XAML Toolkit のインストールについては WPF UI Gallery exhibition #1 を見てください。
一応この連載では MahApps.Metro と Material Design In XAML Toolkit 固有のコントロールや機能は極力使用せず、標準コントロールのみを使用する方針で進める予定です。デザインフレームワークはアプリ完成後でも適用可能ですが、コントロールのマージンなどが標準コントロールとは違う場合も多く、調整が必要な箇所が大量に出て来る可能性もあるので、導入するなら最初から導入しておいた方が手間が少なくて済むと思います。
WPF アプリを今から覚えようと言うならフレームワークの Prism と同時にデザインフレームワークも使い始めて慣れておく方が逆に効率が良いはずだと管理人的には考えています。上に書いた通りこの連載で紹介するサンプルは MahApps.Metro と Material Design In XAML Toolkit を使用して作成しますが、この連載のメインストリームは Prism の使い方紹介なので、デザインフレームワークは不要だと思っている人はインストールしなくても構いません。
今回のまとめと次回の予告のような事
今回は Prism のテンプレートから作成したプロジェクトを起動するまでを紹介しただけですし、次回に繰り越した内容もあります。これ以上のことを紹介するには Prism の DI コンテナへの理解が必須なので、次回は デザインパターンの Dependency Injection と DI を便利に実行するための DI コンテナについて紹介します。
尚、今回紹介したサンプルコードもいつものように GitHub リポジトリ へ上げています。
次回記事「Prism の DI コンテナらは Ioc 上に歌う【step: 4 .NET Core WPF Prism MVVM 入門 2020】」
勉強させていただいております.
src.2とsrc.5で,using ModuleSampleが必要では?
追加したモジュールのnamespaceをPrismSampleに合わせるということですね
【全部の手順が書いてあると勘違いしました.以降の記事でも,かなりの部分が飛ばしてありますね(コードやプロジェクトの構造を自分で解読する作業が必要ですね)】.
分かりにくい文章ですいません…
一応、このエントリの流れは【モジュール追加】⇒【View と VM の命名規則を変更】と言う流れを書きたかったのでわざと省いています。
加えて、管理人の場合はモジュールであろうがクラスライブラリであろうがプロジェクトを追加するとルート名前空間を合わせるのは当然と考えていますが、名前空間の扱いには個人的な好みもあると思うので、そこまで手順に入れるのはどうかな…?と考えてあえて書いていません。(ルート名前空間を合わせたくない人もいると思っています)
手順としては書いていませんが、一言注釈を入れたほうが良さそうな場合は一言書くようにしています。
フレームワークの紹介としては色々な使い方があるはずですが、すべてを網羅して紹介できないのである程度は自身で補完していただく必要はありますが、ソースコードを丸ごと公開しているので、そこから読み取れるだろうと思っているのでご了承ください。
お久しぶりです。https://elf-mission.net/programming/wpf/episode04/にコメントさせていただいたtakkerです。
あのあと一ヶ月で挫折してしまい、しばらくWPF離れていたのですが、必要に迫られてまた戻ってまいりました。
説明が前連載から洗練されていて、よりわかりやすくなっていると感じました。とてもありがたいです。
記事の本筋とは関係ないのですが、気になったのでご報告させていただきます。App.xaml.csのConfigureModuleCatalogの実装についてです。
本記事ではbase.ConfigureModuleCatalogで基底クラスの実装を呼び出していますが、Prism Template PackのPrism Full Appを使って生成したcodeだと、呼び出しが省かれていました。省いてもいいかどうか気になったのでsource codeを覗いてみた所、基底クラスのConfigureModuleCatalogの実装は空になっていました。
○該当するsource code: https://github.com/PrismLibrary/Prism/blob/master/src/Forms/Prism.Forms/PrismApplicationBase.cs#L237
ですので、大した違いではありませんが、base.ConfigureModuleCatalogは省いて問題なさそうです。
とはいえ、今後の開発で基底クラスにも何かしら実装が入る可能性も考えられます。記事のsource codeはbase.ConfigureModuleCatalogを呼び出す形のママで良いかと思います。
長文失礼しました。参考程度に読んでいただけたら幸いです。
takker さん>
お久しぶりです、今回も読んで頂いてありがとうございます。
> 説明が前連載から洗練されていて、よりわかりやすくなっていると感じました。
管理人も多少は成長できているようで安心しましたw
> App.xaml.csのConfigureModuleCatalogの実装
そうですね…
今編集中の step: 5(未公開)では削除しても良いと書いていましたが、ConfigureModuleCatalog の紹介をここにも書いていたのを忘れていましたw
軽く追記しておきます!
追記していただきありがとうございます!
PrismApplication でオーバーライド可能なメソッド
Prism 初期化メソッドの実行順序
このあたり、これまで知りたくてもここまで詳しく日本語で解説して頂いているサイトが見当たらず
ブラックボックス的な理解でモヤモヤしていたので、とてもありがたいです。
comet7360さん >
読んで頂き、コメントまで頂きありがとうございます。
極力他のサイトで見かけない情報を書こうと思っているのでお役に立てたのなら良かったです!
『PrismApplication でオーバーライド可能なメソッド』と『Prism 初期化メソッドの実行順序』は近日公開予定の Prism の DI コンテナを紹介するエントリへ繋げるために入れた内容ですが、反応をもらえたのは嬉しいです!