WPF Prism episode: 3 ~ Re: ゼロから始める Prism 生活 ~

← 前回記事 【WPF Prism episode: 2 ~ WPF のフレームワーク決まってますか?迷ってますか? Prism を選択してもらっていいですか? ~】

前回の記事では、Prism の Module を Shell へ読み込む手順を Prism 公式サンプル を使って紹介したので、今回からは実際にサンプルアプリを作成しながら Prism でよく使用すると思われる機能をどのようにアプリへ組み込むかを順に紹介していく予定です。そして今回は Prism のプロジェクトを作成して Module 内の View を Shell へ表示する所までの方法を紹介しています。

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

Prism 7.1 (WPF) に対応しました!
日本時間 2018/10/16 の未明に Prism 7.1 がリリースされました。
このリリースでは影響が大きい修正が加えられたため Ver.6.3 から変わった部分も併記しています。
注意 Prism for Xamarin.Form の情報はありません

 

(2018/10/26)
Prism 7.1 (WPF) の記述を数か所、追記・修正しました。

新規 Prism プロジェクトの作成

Prism でアプリケーションを作成するには Prism Template Pack をインストールしていると非常に楽なので、未だインストールしていない場合は、episode: 1 を参考にインストールしてください。
重要 本エントリは Prism Template Pack がインストールされている前提で書いています。

Prism のプロジェクトテンプレートを選択

Visual Studio で [新しいプロジェクトの作成] から【新しいプロジェクトダイアログ】を開き、[Visual C#] – [Prism] の順に開いて表示される【Prism Blank App (WPF)】を選択すると、Prism の Shell となる Windows アプリケーションプロジェクトが作成されます。(プロジェクト名は WpfTestApp にしています)

fig.1 新しい Prism プロジェクトの作成

保存場所とフレームワークのバージョンを選択して【OK ボタン】をクリックすると、DI コンテナの選択画面が表示されます。(.NET Framework は Ver.4.7.2 を使用します)

Prism 7.1 (WPF)
Prism 7.1 とほぼ同時に Prism Template Pack もバージョンアップされています。
しかも、バージョンアップしてしまうと以降は 7.1 用の Shell や Module しか作成できなくなるため、6.3 のまま使い続けたい場合は Prism Template Pack をバージョンアップしないよう気を付ける必要があります。
(一応、手作業で修正できそうな気はしますが、DI コンテナ関連の名前空間が根こそぎ変更されているため、アプリ自体の作り次第では、かなり手間がかかる場合があるかもしれません)

Prism で使用する DI コンテナの選択

新しいプロジェクトダイアログでプロジェクト名等を設定して OK ボタンを Click すると、下 fig.2 の DI コンテナ選択画面が表示されます。

fig.2 DI コンテナの選択ダイアログ

Prism では DI コンテナ として、【Autofac】、【Dryloc】、【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 のコンポーネントが存在しないため、新規プロジェクト作成直後は必ず表示されます。

fig.3 Prism 未復元時の警告

警告を消すには、episode: 2 でサンプルを開いたときと同じく、ソリューションのコンテキストメニューから [NuGetパッケージの復元] を選択すると、Prism のコンポーネントが復元されて警告も消えます

fig.4 NuGet から Prism を復元

パッケージの復元が完了すると、警告が消えてビルドできるようになります。

MainWindow のレイアウト

ここで作成する MainWindow は以下のような外観で作成します。

  • 画面上部にツールバーと、画面下部にステータスバーを配置
  • クライアント領域は左右 2 分割
  • クライアント領域の左側には TreeView を配置
  • 左側に配置した TreeView の TreeViewItem をクリックすると右側の View が切り替わる

まずは、Views.MainWindow.xaml を開きます。

fig.5 MainWindow.xaml

クライアント領域に、『Grid』と『ContentControl』がすでに配置されています。
管理人は今までそれなりの数の Windows Form アプリケーションを作成してきたので、この初期デザイン画面を見て、「Grid 要らなくね?」と思い、試しに両コントロールとも削除してみました。

削除したコントロールは非表示コントロールなので、プレビューの見た目は当然変わりません。
そして、MainWindow 内の適当な位置に『TextBox』をツールボックスからドロップして追加してみました。
(コントロールの追加方法は Windows Form と同じです)

fig.6 MainWindow.xaml に TextBox のみ配置

Windows Form の時とあまり見た目は変わりません。
そして、先ほど追加した TextBox の横に『TextBlock』(テキストブロックです!)を配置してみました。

fig.7 追加したコントロールが入れ替わった MainWindow.xaml

先ほど追加したはずの TextBox が消えて、TextBlock に入れ替わってしまいました…
(XAML 側も書き換わっている!?)
知っている人からすれば、「何言ってんだこいつ?」と思われるかもしれませんが、管理人が始めてこの操作をした時は一瞬何が起こったのか理解できませんでした。

疑問に思って調べてみると、WPF の解説サイトでは第一人者(と管理人は思っています)のかずきさんが主宰する【かずきのBlog@hatena】にこんな説明が…

WPF は、コンテンツモデルという初見の人にとってはよくわからないものが採用されています。何か表示するものを 1 つだけ持つものは Content というプロパティで、それを指定します。この Content は object 型なのでなんでも入ったりするという、Windows Forms から見たら考えられない状態になっています。
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 コレクションエディタ】を開きます

fig.8 Grid を行方向に分割

【RowDefinition コレクションエディタ】の追加ボタンを 3 回クリックすると下 fig.9 のように行が 3 つ分追加されます。

fig.9 行方向に 3 分割された Grid

Grid へコントロールを配置

追加されたレイアウトの 1 行目へツールバーコントロールを追加します。
XAML を直接編集せず、ツールボックスからドロップして追加した場合、下 fig.10 のようにコントロールの周りはマージンとして設定されてしまうので、コントロールのコンテキストメニューから  [レイアウト] – [すべてリセット] を選択します。(XAML 側から『Margin』、『Height』等を削除しても同じです)

fig.10 コントロールのマージン等をリセット

レイアウトを全てリセットしたコントロールは Grid の枠一杯に表示されるようになり、Grid 側に設定した比率に従って配置したコントロールが描画されるようになります。
Windows Form で言えば、Dock.Fill を設定したのと同じような見た目です。

レイアウトの 3 行目にステータスバーをツールバーと同様に配置して、両コントロールの Height プロパティを『25』に設定し、Grid コントロールの 1 行目、3 行目の高さを『Auto』に設定します。
WPF ではコントロールを固定サイズに設定することは推奨されていませんが、ここではとりあえず画面の見た目を作ることを優先したいので、固定サイズにしています。
注意 ツールバーやステータスバーについては、配置するだけで使用方法等には触れない予定です。

fig.11 ツールバーとステータスバーを配置した MainWindow.xaml

Grid を入れ子にして行を列方向に分割

レイアウトの 2 行目は列方向に 2 分割して、TreeView を配置する場所を確保したいので、レイアウトの 2 行目へ新規の Grid を追加して入れ子にします
TreeView を配置するクライアント領域は、Windows のエクスプローラーのように右側と左側の比率をユーザがマウス等で変更できるようにしたいので、GridSplitter も追加します。
新規で追加した Grid を列方向に 3 分割して以下の位置にコントロールを追加します。

1 列目TreeView を読み込むための ContentControl
2 列目GridSplitter
3 列目Prism Module を動的に切り替えるための ContentControl

fig.12 Grid コントロールを列方向に分割

MainWindow の最終調整

各コントロール名や、MainWindow 自体のサイズや StartupPosition プロパティ等をお好みで設定して、最終的な XAML は src. 1 のようになります。(MVVM パターンで作成する場合、基本的にコントロール名は不要です)

2018/11/03 修正
22 行目 の GridSplitter で重要なプロパティ(HorizontalAlignment)が抜けていたため追加しました。このプロパティが抜けていると左右のペインが正常にリサイズできません。
詳しくは【[C#][WPF]WPF 垂直GridSplitterのポイント】を参照してください。

21、23 行目に配置した ContentControl.RegionManager.RegionName にリージョン名を指定します。
(ここでは NaviTree と EditorArea にしています)
この状態で 1 度実行してみます。

fig.13 MainWindow の実行イメージ

当たり前ですが、ツールバー、ステータスバー以外はのっぺらぼうの画面が表示されます。

Prism Module の追加

続いて MainWindow に読み込む TreeView を含んだ Prism Module を作成します。

アプリの用途的に考えると TreeView は本来、動的に読み込む必要は無く MainWindow へ固定的に配置する方法もありますが、ここではあえて TreeView を Prism Module へ配置して、アプリ起動時に動的に読み込む仕様で作成することにしました。
TreeView を含むプロジェクトとして、ソリューションに【Prism Module プロジェクト】を追加します。
(プロジェクト名は『NavigationTree』としています。)

fig.14 Prism Module を追加

プロジェクト名は任意ですが、『Module』という単語を末尾に付加するのは避ける方が良いと思います
(理由は後ほど)

Prism Module での DI コンテナ選択画面

Shell プロジェクトを作成したときと同様に DI コンテナの選択画面が表示されるので、Shell で選択したのと同じ DIコンテナ(ここでは Unity)を選択して [CREATE PROJECT ボタン]を Click します。

fig.15 Module の新規作成時も表示される DI コンテナ選択画面(Prism 6.3 まで)

Prism 7.1 (WPF)
Prism 7.1 (WPF) で管理人が最も影響が大きいと感じる変更は DI コンテナ関連です。
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 と言う名前で作成されます。(変更は可能です)

fig.16 Module 追加後のソリューション

最初に Shell のプロジェクトを作成した時と同様に、Prism のパッケージが参照されていないため、[ソリューションのパッケージを復元] の実行が必要です

又、作成したプロジェクトのプロパティから、『ルート名前空間』を Shell のルート名前空間に合わせています。これは単に管理人の好みなので、デフォルトのままでも特に問題はありません。

fig.17 ルート名前空間

Prism テンプレートから自動生成された View 名を簡単に変更する

Module プロジェクトを作成すると一緒に【Views.ViewA】と言う UserControl が自動生成されます。
サンプルアプリだと言っても、さすがに名前が微妙過ぎなので変更したいですが View 自体の名前を直接変更するより View を新規追加した方が早いので【Views】フォルダのコンテキストメニューから【Prism UserControl (WPF)】を追加します。(ここでは NavigationTree としました)

fig.18 Prism Module に View を追加する

追加先に【Viewsフォルダ】を指定して View を追加すると、ViewModel も併せて追加してくれます

本来 View の名前を変更する場合は、以下全ての名前を変更する必要があります。

  • View のファイル名
  • View のコードビハインド
  • View の Xaml
  • ViewModel のファイル名
  • ViewModel のコード

変更が必要な箇所が多いので変更するよりは新規で追加して、元のファイルを削除した方が楽です。

fig.19 Prism Module へ新規 View 追加後のソリューションエクスプローラー

元々 Prism の テンプレートから自動追加される【ViewA】は不要になるので、ViewModel と併せて削除すると View 名を変更したのと同じ結果が得られます。

追加した UserControl をデザイナー画面で見るとレイアウトが崩れる場合の対応

追加した Prism UserControl をデザイナで開いて適当にコントロール等を配置していると、思ったレイアウトにならない場合があります。
例えば下 fig.20 のキャプチャのような極小の UserControl が表示されたり

fig.20 極小サイズの UserControl

極小の UserControl が表示される以外にも、下 fig.21 のように Grid の行定義へ比率をちゃんと指定しているにも関わらず、デザイナー画面での見た目が崩れる場合もあります。
(下図は Grid の行を 50%、50% で指定しているが 2 行目が 50% に見えない)

fig.21 行高さの比率がおかしい Grid コントロール

実行すると想像と変わらない画面が表示されると思いますが、考えている画面レイアウトがデザイナー画面で再現されないのは困ります。少なくとも管理人は。
このような場合は、src. 2 の XAML へハイライトを付けた 4 行を追加することで解消できます。

src. 3 の 4 行はデザイナー画面での UserControl のサイズを設定する項目で、実行時には無視されます
コピペしやすいよう別枠で置いておきます。

この 4 行を追加すると、土台となる UserControl が指定したサイズで表示されるようになるため、デザイン時に実行時のイメージがつかみやすくなると思います。
デザイナー画面で UserControl の見た目が崩れて困っている場合は、UserControl の定義に上記の 4 行が記述されているかを確認してみてください。

これは、Prism Template Pack の問題で、【Prism Module プロジェクトの追加】から自動生成される View には上記 4 行が記述された状態で作成されますが、新しい項目の追加から View を追加した場合は追加されないことが原因です。
何故そのような状態になっているのか分かりませんが、次のリリースで治ることを期待しましょうw

Prism 7.1 (WPF)
Prism Template Pack もバージョンアップされましたが、対応されていませんでした…

RegisterViewWithRegion メソッドで View を Shell に表示する

本題に戻ります。
追加した View(UserControl)をデザイナで開いて、TreeView を追加して Grid 一杯になるようレイアウトを全てリセットした後のデザイナ画面です。

fig.22 TreeView を追加した Prism の View

とりあえずここではこれ以上何もせず、保存します。

Module 初期化時に NavigationTree を Shell のリージョンへ読み込むには、NavigationTreeModule に src. 4 のハイライト行を追加します。

Module の Initialize メソッドで IRegionManager.RegisterViewWithRegion を呼び出すと、Module 初期化時に第 1 パラメータで指定した Region へ第 2 パラメータで指定した型の View を読み込め!と言う意味になります。

Prism 7.1 では Module の Initialize メソッドが削除されていて、代わりに OnInitialized メソッドへ src. 5 のように記述すると 6.3 と同様の動作になります

Prism 7.1 (WPF)
Prism 7.1 からは DI コンテナを抽象化したため、IModule の構造も変更されています。
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 の参照設定を追加する必要があります。

Prism 6.3 まではオーバーライドした ConfigureModuleCatalog メソッド内で ModuleCatalog(Bootstrapper 自体のプロパティ)にロードしたい Module を AddModule して読み込む Module を Shell へ登録(通知)しています。

Prism 7.1 (WPF)
Prism 7.1 (WPF) 以降で Bootstrapper には Obsolete 属性が追加されました
(Prism 内にクラスは残っていますが、使用は推奨されません)

Bootstrapper の代わりに、【PrismApplication】が定義され、App.xaml が継承しています。
7.1 での App.xaml.cs は Bootstrapper に記述していいたのと同じ内容を src. 7 のように記述します。
(記述内容は Bootstrapper とほとんど同じです)

そして実行すると、TreeView(枠だけですが…)が読み込まれた画面が表示されます。

fig.23 TreeView を追加した MainWindow

いかがでしょう?
途中脱線もしたので、記事の文字数は多かったと思いますが、Module に配置した View を Prism Shell へ表示するために実際に記述するコード量は結構少なく済むと感じてもらえるとありがたいです。
この Module の動的読込を利用するだけでも Prism を採用する価値がある!と管理人は声を大にして言いたいと思っています。

Prism 7.1 (WPF)
既存プロジェクトの Prism を 7.1 にアップしたら突然大量のコンパイルエラーが発生したので、一時はどうなるかと思いましたが、とりあえずプロジェクトを作り直して対応できました。
(ここに書いた対応が正しいかは未だ分かりませんが…)

 

(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 リポジトリ に上げておきます。

 

この連載記事について(2018/11/26 追記)
WPF は .NET Framework 3.0 と共に 2006 年 11 月にリリースされて 10 年以上(2018 年 11 月現在)経つプラットフォームですが、今から WPF について勉強しようとしても、ネットで検索できる情報は Windows Form 関連の情報と比べて圧倒的に少なく(特に日本語の情報)又、体系的に書かれた WPF 入門的な情報もほとんど見当たりません。
しかも、MVVM パターンでフレームワークを使用して作成するとなると、更にヒットする情報は少なくなるため学習に困難な状況だと言えると思います。

加えて、MVVM パターンは WPF より遅れて普及したため、ネットで見つかる WPF の情報はコードビハインドを想定して書かれたものが多く、欲しい情報が見つからず MVVMパターンでの開発に挫折した人も多いのではないでしょうか。

管理人個人的には、MVVM で開発する場合、海外も含めて 2015 年より前に書かれた情報は MVVM パターンでの開発に適用できない情報が 8 割を超えると感じています。
その上、WPF への機能追加や、MVVM パターン内でも手法のトレンドが以前とは変わっているものもあり、ネットの情報はかなりカオスな状態で何から手を付けていいか途方に暮れそうになったことも 1 度や 2 度ではありませんでした。
そんな苦労した経験から、「自分が WPF アプリを作成するために調べた情報を、調べた順番に書けば役に立つ人も居るかもしれない!」と思ってこの記事を書き始めました。

この連載記事は、WPF 入門、Prism 入門として読んで欲しいと思って書いていますが、コメントやいいね等をもらったことがないので、実際、この記事が役に立っているのかは分かりません。
ただ、このサイトのアクセスログからは、この連載のいずれかの記事を見た人が他の episode も読んでくれる場合が多い傾向が見える為、これから WPF や Prism を覚えたい人、WPF や Prism で何ができるか知りたい人の何らかの助けになっていれば幸いと考えています。

【WPF Prism episode: 4 ~ DI だけど Unity さえあれば関係ないよねっ~】次回記事 →

 

あわせて読みたい

コメントを残す

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

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

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