WPF Prism episode: 3 ~ Re: ゼロから始める Prism 生活 ~
前回の記事では、Prism の Module を Shell へ読み込む手順を Prism 公式サンプル を使って紹介したので、今回からは実際にサンプルアプリを作成しながら Prism でよく使用すると思われる機能をどのようにアプリへ組み込むかを順に紹介していく予定です。そして今回は Prism のプロジェクトを作成して Module 内の View を Shell へ表示する所までの方法を紹介しています。
2020/6/5 追記
新たに 2020 年度版の WPF 入門 の連載をスタートしました!
2020/7/17 追記
WPF Prism episode シリーズ全体を整理して再構成した 2020 年度版の WPF 入門で、このエントリと同じ辺りの内容を紹介した .NET Core WPF Prism MVVM 入門 2020 Step: 3 を公開しました。
尚、この記事は Visual Studio 2017 Community Edition で .NET Framework 4.7.2 以上 と C# + Prism 7.1 を使用して 、WPF アプリケーションを MVVM パターンで作成するのが目的で、C# での基本的なコーディング知識を持っている人が対象です。
日本時間 2018/10/16 の未明に Prism 7.1 がリリースされました。
このリリースでは影響が大きい修正が加えられたため Ver.6.3 から変わった部分も併記しています。
注意 Prism for Xamarin.Form の情報はありません
(2018/10/26)
Prism 7.1 (WPF) の記述を数か所、追記・修正しました。
日本時間 2019/7/25 の未明に Prism 7.2 がリリースされました。
Prism 7.2 で作成する場合の注意事項も追記しています。
追記:2019/8/10
目次
新規 Prism プロジェクトの作成
Prism でアプリケーションを作成するには Prism Template Pack をインストールしていると非常に楽なので、未だインストールしていない場合は、episode: 1 を参考にインストールしてください。
重要 本エントリは Prism Template Pack がインストールされている前提で書いています。
Prism のプロジェクトテンプレートを選択
Visual Studio で [新しいプロジェクトの作成] から【新しいプロジェクトダイアログ】を開き、[Visual C#] – [Prism] の順に開いて表示される【Prism Blank App (WPF)】を選択すると、Prism の Shell となる Windows アプリケーションプロジェクトが作成されます。(プロジェクト名は WpfTestApp にしています)
保存場所とフレームワークのバージョンを選択して【OK ボタン】をクリックすると、DI コンテナの選択画面が表示されます。(.NET Framework は Ver.4.7.2 を使用します)
Prism 7.1 とほぼ同時に Prism Template Pack もバージョンアップされています。
しかも、バージョンアップしてしまうと以降は 7.1 用の Shell や Module しか作成できなくなるため、6.3 のまま使い続けたい場合は Prism Template Pack をバージョンアップしないよう気を付ける必要があります。
(一応、手作業で修正できそうな気はしますが、DI コンテナ関連の名前空間が根こそぎ変更されているため、アプリ自体の作り次第では、かなり手間がかかる場合があるかもしれません)
Prism で使用する DI コンテナの選択
新しいプロジェクトダイアログでプロジェクト名等を設定して OK ボタンを Click すると、下 fig.2 の DI コンテナ選択画面が表示されます。
Prism では DI コンテナ として、【Autofac】、【DryIoc】、【Unity】の 3 種類から選択できますが、ここでは【Unity】を選択して [CREATE PROJECT ボタン] を Click します。(.NET Framework Ver.4.6 等を選択すれば、【Ninject】も選択できるようですが、.NET Framework Ver.4.7.2 を選択した場合、Ninject は選択肢に表示されませんでした)
info DI とは Dependency Injection(依存性注入)のことで、Prism 内部では DI コンテナを使用して View、ViewModel の関連付け等を実装しているようです。
DI コンテナの【Unity】は ゲーム開発用エンジンと同じ名前ですが全くの別物です。DI コンテナの Unity は Prism と同じく Microsoft の patterns & practices で生まれたプロジェクトで、日本では最も知名度の高い DI コンテナだと思います。管理人は Unity 以外の DI コンテナは使ったことがないので、他の DI コンテナと比べたメリット、デメリット等は紹介できませんが、日本語での情報の取りやすさは 3 つの DI コンテナの中でトップだと思っています。
Prism を NuGet から復元
DI コンテナ選択後は Prism プロジェクトがテンプレートから作成され Visual Studio に読み込まれるとビルドエラー警告が表示されます。
注意 この警告は作成したプロジェクト内に Prism のコンポーネントが存在しないため、新規プロジェクト作成直後は必ず表示されます。
警告を消すには、episode: 2 でサンプルを開いたときと同じく、ソリューションのコンテキストメニューから [NuGetパッケージの復元] を選択すると、Prism のコンポーネントが復元されて警告も消えます。
パッケージの復元が完了すると、警告が消えてビルドできるようになります。
今回 Prism Template Pack はアップデートされないようなので、Prism Template Pack Ver.2.1.6 で作成すると Prism 7.1 が復元されます。
Prism 7.2 で作成する場合は Prism 復元後、【ソリューションの Nuget パッケージの管理】から Prism 7.2 へ手作業で更新する必要があります。
Prism 7.2 ではダイアログウィンドウを MVVM パターンで簡単に表示できる IDialogService が追加されているため、Prism 7.2 の使用をお勧めします。
IDialogService の使用法は『WPF Prism episode: 16 ~ Prism7.2、ダイアログは IDialogService でって言ったよね! ~』で紹介しているのでよろしければどうぞ。
追記:2019/8/10
MainWindow のレイアウト
ここで作成する MainWindow は以下のような外観で作成します。
- 画面上部にツールバーと、画面下部にステータスバーを配置
- クライアント領域は左右 2 分割
- クライアント領域の左側には TreeView を配置
- 左側に配置した TreeView の TreeViewItem をクリックすると右側の View が切り替わる
まずは、Views.MainWindow.xaml を開きます。
クライアント領域に、『Grid』と『ContentControl』がすでに配置されています。
管理人は今までそれなりの数の Windows Form アプリケーションを作成してきたので、この初期デザイン画面を見て、「Grid 要らなくね?」と思い、試しに両コントロールとも削除してみました。
削除したコントロールは非表示コントロールなので、プレビューの見た目は当然変わりません。
そして、MainWindow 内の適当な位置に『TextBox』をツールボックスからドロップして追加してみました。
(コントロールの追加方法は Windows Form と同じです)
Windows Form の時とあまり見た目は変わりません。
そして、先ほど追加した TextBox の横に『TextBlock』(テキストブロックです!)を配置してみました。
先ほど追加したはずの TextBox が消えて、TextBlock に入れ替わってしまいました…
(XAML 側も書き換わっている!?)
知っている人からすれば、「何言ってんだこいつ?」と思われるかもしれませんが、管理人が始めてこの操作をした時は一瞬何が起こったのか理解できませんでした。
疑問に思って調べてみると、WPF の解説サイトでは第一人者(と管理人は思っています)のかずきさんが主宰する【かずきのBlog@hatena】にこんな説明が…
Windows FormsからWPFへ乗り換えるときの最初の障壁
と言う事は、Content プロパティを持つコントロールに置けるコントロールは 1 つだけと言う事になり、Content プロパティを持つ『WPF の Window クラスはコンテナではない』ことになります。
note 逆に言えば、Content プロパティを持つコントロールにパネル等を置けば、その中に複数のコントロールを配置することが可能と言うことになります。
まあ、Window クラスがコンテナとして機能しないことに何か問題があるかと言えば、すぐには思い付かないのも確かですが、このような Windows Form と根本的に違う部分などをまとめた情報が目立つ場所で目にすることがあまりない事が WPF が普及しない原因だと思うのは、管理人だけでしょうか…?
実際、XAML 構文やデータバインディングの方法等は重要な情報でしょうし、Microsoft 的には WPF における目玉機能なのでしょうが、開発者が新技術を目にした時、最も知りたい事は、自分がアプリケーションを作成する際に、どこに注意すれば良いか?今までのやり方とどれだけ違うのか?という点ではないかと管理人は思います。
ただ、WPF にしても Prism にしても最近ようやく情報が熟れて来た感がありますし、Xamarin で WPF も作成できるようになったという追い風もあるので、管理人個人的にはそろそろ普及し始めて欲しいと思っています。
又、あくまで管理人の個人的な意見ですが、おそらく UWP は WPF 以上に普及せず廃れていくと思っているので、今から WPF を勉強するのは決して無駄ではないとも思っています。
Grid コントロールで Window のレイアウトを作成
Windows Form で画面をレイアウトしていた時もパネルと Dock プロパティを組合せてリサイズに対応した画面を作成することができました。(管理人は主にパネルを多用してレイアウトしていましたが、TableLayoutPanel 等を使用していた人も多いかもしれません)
WPF でコントロールを配置する場合も基本の考え方は変わりません。
横方向のグループ・縦方向のグループに分けて、グループ単位でパネルに配置する考え方自体は特に変わらないと思います。(固定サイズ画面のみ作成する事が圧倒的に多い業務系の開発経験しかない人には、馴染みが薄いのかもしれませんが…)
ただし、WPF では Windows Form にある Anchor や Dock プロパティ等は存在しないので、基本的にはコンテナ系のパネルを組み合わせて(又は入れ子にして)レイアウトを作っていくことになります。
例えると、CSS 登場以前の Web サイトデザインでよく見られた、Table タグを入れ子にして位置を調整するのと似ています。
Grid コントロールを行方向に分割
コントロールの配置に Grid(等のコンテナ)が必要なことは理解できたので、Grid 上にコントロールを配置していきます。
実際にコントロールを配置する前に、まず Prism テンプレートが自動的に配置した ContentControl を削除(コメントアウト)して、ツールバー、ステータスバーを配置するために Grid を行方向に 3 分割します。
Grid を行方向に分割するには、[Grid のプロパティ] – [レイアウト(カテゴリ)] – [RowDefinitions] のボタンを Click して【RowDefinition コレクションエディタ】を開きます。
【RowDefinition コレクションエディタ】の追加ボタンを 3 回クリックすると下 fig.9 のように行が 3 つ分追加されます。
Grid へコントロールを配置
追加されたレイアウトの 1 行目へツールバーコントロールを追加します。
XAML を直接編集せず、ツールボックスからドロップして追加した場合、下 fig.10 のようにコントロールの周りはマージンとして設定されてしまうので、コントロールのコンテキストメニューから [レイアウト] – [すべてリセット] を選択します。(XAML 側から『Margin』、『Height』等を削除しても同じです)
レイアウトを全てリセットしたコントロールは Grid の枠一杯に表示されるようになり、Grid 側に設定した比率に従って配置したコントロールが描画されるようになります。
Windows Form で言えば、Dock.Fill を設定したのと同じような見た目です。
レイアウトの 3 行目にステータスバーをツールバーと同様に配置して、両コントロールの Height プロパティを『25』に設定し、Grid コントロールの 1 行目、3 行目の高さを『Auto』に設定します。
WPF ではコントロールを固定サイズに設定することは推奨されていませんが、ここではとりあえず画面の見た目を作ることを優先したいので、固定サイズにしています。
注意 ツールバーやステータスバーについては、配置するだけで使用方法等には触れない予定です。
Grid を入れ子にして行を列方向に分割
レイアウトの 2 行目は列方向に 2 分割して、TreeView を配置する場所を確保したいので、レイアウトの 2 行目へ新規の Grid を追加して入れ子にします。
TreeView を配置するクライアント領域は、Windows のエクスプローラーのように右側と左側の比率をユーザがマウス等で変更できるようにしたいので、GridSplitter も追加します。
新規で追加した Grid を列方向に 3 分割して以下の位置にコントロールを追加します。
1 列目 | TreeView を読み込むための ContentControl |
2 列目 | GridSplitter |
3 列目 | Prism Module を動的に切り替えるための ContentControl |
MainWindow の最終調整
各コントロール名や、MainWindow 自体のサイズや StartupPosition プロパティ等をお好みで設定して、最終的な XAML は src. 1 のようになります。(MVVM パターンで作成する場合、基本的にコントロール名は不要です)
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 | <Window x:Class="WpfTestApp.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" WindowStartupLocation="CenterScreen"> <Grid x:Name="BaseGrid"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ToolBar x:Name="MainToolBar" Height="25"/> <StatusBar x:Name="MainStatus" Grid.Row="2" Height="25"/> <Grid x:Name="ClientGrid" Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.3*"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="0.7*"/> </Grid.ColumnDefinitions> <ContentControl x:Name="NaviTree" prism:RegionManager.RegionName="NaviTree" Grid.Column="0"></ContentControl> <GridSplitter Grid.Column="1" Width="3" HorizontalAlignment="Stretch"/> <ContentControl x:Name="EditorArea" prism:RegionManager.RegionName="EditorArea" Grid.Column="2"></ContentControl> </Grid> </Grid> </Window> |
詳しくは【[C#][WPF]WPF 垂直GridSplitterのポイント】を参照してください。
21、23 行目に配置した ContentControl.RegionManager.RegionName にリージョン名を指定します。
(ここでは NaviTree と EditorArea にしています)
この状態で 1 度実行してみます。
当たり前ですが、ツールバー、ステータスバー以外はのっぺらぼうの画面が表示されます。
Prism Module の追加
続いて MainWindow に読み込む TreeView を含んだ Prism Module を作成します。
アプリの用途的に考えると TreeView は本来、動的に読み込む必要は無く MainWindow へ固定的に配置する方法もありますが、ここではあえて TreeView を Prism Module へ配置して、アプリ起動時に動的に読み込む仕様で作成することにしました。
TreeView を含むプロジェクトとして、ソリューションに【Prism Module プロジェクト】を追加します。
(プロジェクト名は『NavigationTree』としています。)
プロジェクト名は任意ですが、『Module』という単語を末尾に付加するのは避ける方が良いと思います。
(理由は後ほど)
Prism Module での DI コンテナ選択画面
Shell プロジェクトを作成したときと同様に DI コンテナの選択画面が表示されるので、Shell で選択したのと同じ DIコンテナ(ここでは Unity)を選択して [CREATE PROJECT ボタン]を Click します。
Prism 7.1 から DI コンテナが抽象化され、Unity や MEF 等のコンテナを直接指定して呼び出す必要がなくなっています。
そのため Shell プロジェクト作成時は Prism 6.3 の時と同じく DI コンテナの選択画面が表示されますが、Module プロジェクトを作成する場合は上 fig.15 の DI コンテナ選択画面は表示されません。
Prism Module 追加後のソリューション
Module 追加後のソリューションは以下のようになっていて、Module プロジェクト(NavigationTree)のルートには Prism の IModule を継承した【NavigationTreeModule】が自動追加されます。
先ほど『Module』という単語は避けたほうがいいと書いたのはこの Module クラス名が理由で、このクラスは『プロジェクト名 + Module』という名前で自動生成されるため、プロジェクト名を NaviModule にすると NaviModuleModule と言う名前で作成されます。(変更は可能です)
最初に Shell のプロジェクトを作成した時と同様に、Prism のパッケージが参照されていないため、[ソリューションのパッケージを復元] の実行が必要です。
又、作成したプロジェクトのプロパティから、『ルート名前空間』を Shell のルート名前空間に合わせています。これは単に管理人の好みなので、デフォルトのままでも特に問題はありません。
Prism テンプレートから自動生成された View 名を簡単に変更する
Module プロジェクトを作成すると一緒に【Views.ViewA】と言う UserControl が自動生成されます。
サンプルアプリだと言っても、さすがに名前が微妙過ぎなので変更したいですが View 自体の名前を直接変更するより View を新規追加した方が早いので【Views】フォルダのコンテキストメニューから【Prism UserControl (WPF)】を追加します。(ここでは NavigationTree としました)
追加先に【Viewsフォルダ】を指定して View を追加すると、ViewModel も併せて追加してくれます。
本来 View の名前を変更する場合は、以下全ての名前を変更する必要があります。
- View のファイル名
- View のコードビハインド
- View の Xaml
- ViewModel のファイル名
- ViewModel のコード
変更が必要な箇所が多いので変更するよりは新規で追加して、元のファイルを削除した方が楽です。
元々 Prism の テンプレートから自動追加される【ViewA】は不要になるので、ViewModel と併せて削除すると View 名を変更したのと同じ結果が得られます。
追加した UserControl をデザイナー画面で見るとレイアウトが崩れる場合の対応
追加した Prism UserControl をデザイナで開いて適当にコントロール等を配置していると、思ったレイアウトにならない場合があります。
例えば下 fig.20 のキャプチャのような極小の UserControl が表示されたり。
極小の UserControl が表示される以外にも、下 fig.21 のように Grid の行定義へ比率をちゃんと指定しているにも関わらず、デザイナー画面での見た目が崩れる場合もあります。
(下図は Grid の行を 50%、50% で指定しているが 2 行目が 50% に見えない)
実行すると想像と変わらない画面が表示されると思いますが、考えている画面レイアウトがデザイナー画面で再現されないのは困ります。少なくとも管理人は。
このような場合は、src. 2 の XAML へハイライトを付けた 4 行を追加することで解消できます。
1 2 3 4 5 6 7 8 9 | <UserControl x:Class="BlankApp1.Views.CalculatePanel" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:prism="http://prismlibrary.com/" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" prism:ViewModelLocator.AutoWireViewModel="True"> |
src. 3 の 4 行はデザイナー画面での UserControl のサイズを設定する項目で、実行時には無視されます。
コピペしやすいよう別枠で置いておきます。
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" |
この 4 行を追加すると、土台となる UserControl が指定したサイズで表示されるようになるため、デザイン時に実行時のイメージがつかみやすくなると思います。
デザイナー画面で UserControl の見た目が崩れて困っている場合は、UserControl の定義に上記の 4 行が記述されているかを確認してみてください。
これは、Prism Template Pack の問題で、【Prism Module プロジェクトの追加】から自動生成される View には上記 4 行が記述された状態で作成されますが、新しい項目の追加から View を追加した場合は追加されないことが原因です。
何故そのような状態になっているのか分かりませんが、次のリリースで治ることを期待しましょうw
RegisterViewWithRegion メソッドで View を Shell に表示する
本題に戻ります。
追加した View(UserControl)をデザイナで開いて、TreeView を追加して Grid 一杯になるようレイアウトを全てリセットした後のデザイナ画面です。
とりあえずここではこれ以上何もせず、保存します。
Module 初期化時に NavigationTree を Shell のリージョンへ読み込むには、NavigationTreeModule に src. 4 のハイライト行を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using Microsoft.Practices.Unity; using Prism.Modularity; using Prism.Regions; namespace WpfTestApp { public class NavigationTreeModule : IModule { private IRegionManager _regionManager; private IUnityContainer _container; public NavigationTreeModule(IUnityContainer container, IRegionManager regionManager) { _container = container; _regionManager = regionManager; } public void Initialize() { this._regionManager.RegisterViewWithRegion("NaviTree", typeof(Views.NavigationTree)); } } } |
Module の Initialize メソッドで IRegionManager.RegisterViewWithRegion を呼び出すと、Module 初期化時に第 1 パラメータで指定した Region へ第 2 パラメータで指定した型の View を読み込め!と言う意味になります。
Prism 7.1 では Module の Initialize メソッドが削除されていて、代わりに OnInitialized メソッドへ src. 5 のように記述すると 6.3 と同様の動作になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | namespace WpfTestApp { public class NavigationTreeModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionMan = containerProvider.Resolve<IRegionManager>(); regionMan.RegisterViewWithRegion("NaviTree", typeof(Views.NavigationTree)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } } |
6.3 までは コンストラクタへ IRegionManager をインジェクションして、View と Region の関連付けは Module の Initialize メソッドで行っていましたが、7.1 からは IModule の OnInitialized メソッドに【containerProvider】パラメータが渡されるため、IRegionManager を Module にインジェクションする必要がなくなりました。
その代わり、DI コンテナから Resolve メソッドで IRegionManager を引っ張り出さなければならなくなったので、結果的にコードの記述量は変わらないと思います。
Prism 7.1 から変更されたと言ってもコンストラクタへインジェクションする方法は 6.3 と同じなので 6.3 の頃と同じように IRegionManager をコンストラクタへインジェクションして、OnInitialized メソッドで View を登録することも今まで通り可能です。
(2018/10/26 追記)
補足 Prism Module で DI コンテナへ型やインスタンスを登録したい場合、Prism 7.1 で追加された RegisterTypes メソッドの containerRegistry パラメータを使用して登録できます。
Bootstrapper(6.3 まで)と PrismApplication (7.1 から)に設定する内容
最後に Shell に読み込む Module を指定するむための記述を Bootstrapper に追加します。
重要 以下を実装する前に、Shell へ Module の参照設定を追加する必要があります。
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 | using Microsoft.Practices.Unity; using Prism.Modularity; using Prism.Unity; using System.Windows; using WpfTestApp.Views; namespace WpfTestApp { class Bootstrapper : UnityBootstrapper { protected override DependencyObject CreateShell() { return Container.Resolve<MainWindow>(); } protected override void InitializeShell() { Application.Current.MainWindow.Show(); } protected override void ConfigureModuleCatalog() { var moduleCatalog = (ModuleCatalog)ModuleCatalog; moduleCatalog.AddModule(typeof(NavigationTreeModule)); } } } |
Prism 6.3 まではオーバーライドした ConfigureModuleCatalog メソッド内で ModuleCatalog(Bootstrapper 自体のプロパティ)にロードしたい Module を AddModule して読み込む Module を Shell へ登録(通知)しています。
Prism 7.1 (WPF) 以降で Bootstrapper には Obsolete 属性が追加されました。
(Prism 内にクラスは残っていますが、使用は推奨されません)
Bootstrapper の代わりに、【PrismApplication】が定義され、App.xaml が継承しています。
7.1 での App.xaml.cs は Bootstrapper に記述していいたのと同じ内容を src. 7 のように記述します。
(記述内容は Bootstrapper とほとんど同じです)
src. 6 の上にも書いていますが、コメントで指摘して頂いたため↓を追記します。
重要 src. 7 を記述する前に、Shell へ Module の参照設定を追加する必要があります。
Prism は既に 7 系がメインなので 6.3 の所だけに書いているのは確かに分かりにくいですね…
6.3 と 7 系が混ざっていて読みにくいので新しく書き直すことを考えた方が良いかもしれません。(2020/3/28 追記)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | namespace WpfTestApp { /// <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) { } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<NavigationTreeModule>(InitializationMode.WhenAvailable); } } } |
そして実行すると、TreeView(枠だけですが…)が読み込まれた画面が表示されます。
いかがでしょう?
途中脱線もしたので、記事の文字数は多かったと思いますが、Module に配置した View を Prism Shell へ表示するために実際に記述するコード量は結構少なく済むと感じてもらえるとありがたいです。
この Module の動的読込を利用するだけでも Prism を採用する価値がある!と管理人は声を大にして言いたいと思っています。
(ここに書いた対応が正しいかは未だ分かりませんが…)
(2018/10/26 追記)
後は、Module 内で DI コンテナをどのように使うかが見つかっていませんが、調査を続けていく予定です。
2018/10/26 時点でこの記事へのアクセスが増大してきている為、後で判明した点を追記しておきます。
(次回記事は鋭意作成中でまだ公開できないので…)
Prism 7.1 で最も影響の大きな変更は DI コンテナの取り扱いで、『UnityContainer』等の直接的なクラス名を使用するのではなく、DI コンテナを抽象化した【IContainerRegistry】、【IContainerProvider】を使用して、型・インスタンスの登録、取り出しを行うようになった点です。
名前の通り、【IContainerRegistry】で型・インスタンスの登録を行い、【IContainerProvider】で型・インスタンスを取り出します。
Bootstrapper と Module の変更が強制されますが、DI コンテナ自体の動作は Prism 6.3 から変わっていません。
例えば、ViewModel 等のコンストラクタに DI コンテナへ登録した型をパラメータとして追加すると、今までと変わらずインジェクションされます。(private や public や static に置いた DI コンテナをいろんな所からいじりまわしているような行儀の悪い作りだと困りそうですが…)
管理人的には、Module で IRegionManager に RegisterViewWithRegion する箇所、VM でインスタンス登録用に IUnityContainer をインジェクションしている箇所(インジェクションする型を IContainerRegistry に変更する必要がある)は修正必須だと思いますが、それ以外は Prism 6.3 から変更なしで動作すると思います。
とりあえず、今回はここまでとして次回は
- TreeView に TreeViewItem を表示する
- Prism で使う Unity
の2本立てでお送りする予定です。
又、ここまで作成したソリューションを GitHub リポジトリ に上げておきます。
2020/6/5 追記
新たに 2020 年度版の WPF 入門 の連載をスタートしました!
2020/7/17 追記
WPF Prism episode シリーズ全体を整理して再構成した 2020 年度版の WPF 入門で、このエントリと同じ辺りの内容を紹介した .NET Core WPF Prism MVVM 入門 2020 Step: 3 を公開しました。
しかも、MVVM パターンでフレームワークを使用して作成するとなると、更にヒットする情報は少なくなるため学習に困難な状況だと言えると思います。
加えて、MVVM パターンは WPF より遅れて普及したため、ネットで見つかる WPF の情報はコードビハインドを想定して書かれたものが多く、欲しい情報が見つからず MVVMパターンでの開発に挫折した人も多いのではないでしょうか。
管理人個人的には、MVVM で開発する場合、海外も含めて 2015 年より前に書かれた情報は MVVM パターンでの開発に適用できない情報が 8 割を超えると感じています。
その上、WPF への機能追加や、MVVM パターン内でも手法のトレンドが以前とは変わっているものもあり、ネットの情報はかなりカオスな状態で何から手を付けていいか途方に暮れそうになったことも 1 度や 2 度ではありませんでした。
そんな苦労した経験から、「自分が WPF アプリを作成するために調べた情報を、調べた順番に書けば役に立つ人も居るかもしれない!」と思ってこの記事を書き始めました。
この連載記事は、WPF 入門、Prism 入門として読んで欲しいと思って書いていますが、コメントやいいね等をもらったことがないので、実際、この記事が役に立っているのかは分かりません。
ただ、このサイトのアクセスログからは、この連載のいずれかの記事を見た人が他の episode も読んでくれる場合が多い傾向が見える為、これから WPF や Prism を覚えたい人、WPF や Prism で何ができるか知りたい人の何らかの助けになっていれば幸いと考えています。
次回記事「DI だけど Unity さえあれば関係ないよねっ【episode: 4 WPF Prism】」
本サイトで勉強させて頂いています。
まだまだ初心者で分からないことが多すぎるのですが、ウインドの最小化または最大化をprismで行う方法をご存知なら教えて欲しいです。
ウインドを閉じる方法はYoutube(https://youtu.be/U7Qclpe2joo )で見つけたのですが最小化(最大化)が見当たらず自分で考えても上手くいかないので困っています。
コメント失礼します。
UserControlを追加すると、ViewModelも自動生成されるはずです。
しかし、ViewModelが自動生成されない現象に陥っています。
何か原因をご存知でしょうか?
ご教授いただけますと幸いです。
Prism で UserControl を追加すると自動で VM が生成されるのは、デフォルトの命名規則を使用していて、UserControl を Views フォルダに追加した時だけだと思います。
View、VM の命名規則を変更されていませんか?
詳しくは .NET Core WPF Prism MVVM 入門 step: 3 の『5.3 View と VM を関連付ける命名規則を変更』に書いているので見てみて下さい。
https://gyazo.com/b9d0e6b43608989e3a4471593314a4fe
こちらではなく、
https://gyazo.com/ba3db4af66f0f37cd617d23a0c9e83e2
こちらを選択していました。
凡ミスです。。貴重なお時間を頂戴してしまい、すみませんでした。。。
WPF 標準の UserControl を選択してたんですね!
解決したなら何よりです!
世の中フレームワークが豊富ではありますが、逆に言うと氾濫している状況の中で良い記事に出会えました。
選択の参考にさせていただきます!
読んで頂き & コメントも頂きありがとうございます。
現在、新規連載中の .NET Core WPF MVVM 入門 2020 step: 2 でも最近のフレームワークを紹介しているので良ければそちらも見てください!
Prismの勉強に大変役に立っております。
こういう記事がないと勉強が難しいので助かります。
jumbo さん>
読んで頂いて & コメントまで頂いてありがとうございます。
そう言って頂けるとブログを続けるモチベーションになるのでこちらこそ非常に助かります。
2年程前、WPF Prismに挫折してしまいましたが、たまたまこのサイトを見て、再度取り組むことにしました。貴重な情報があり、今回は挫折しなくて良さそうです。非常にありがたいサイトだと思っています。ところで、私の見逃しがあるかもしれないのですが、App.xaml.csの20行目のNavigationTreeModuleを参照できないというエラーが出てしまい、https://qiita.com/no13/items/2ec95dfb1b7461125b23を参考にして、プロジェクトに参照設定を実施したところエラーがなくなり、実行できました。
コメントありがとうございます。
一応、第 5 章 src. 6 の上側に書いていますが、「Prism 6.3 の場合だけは必要」のようにも見えますね…
この episode: 3 は Prism 6.3 と 7.1 が混在していて分かりにくいと思うので指摘して頂いて助かりました。
ありがとうございます。src. 7 の上部にも追記しておきました。
この episode はその内書き直した方が良いかな… と決意が湧きましたw
WPF、Prism、MVVM勉強中・・・
結構助かります!
showeryさん
コメントありがとうございます。そう言っていただけるとブログを書いている甲斐があると言うもんです。
今後ともよろしくお願いします。