PrismでFeatureフォルダを追加してアプリケーションを構成する
印象としては,ASP.NET CoreでいうところのArea機能に似たような感じなのかな.
Prismで使いやすいようにConfigureViewModelLocator
に手を加えている.
具体的には以下のように機能ごとにフォルダを分ける方針.
│ App.xaml │ App.xaml.cs │ └───Features │ │ │ └───Dashboard │ │ Model │ │ Services │ │ ViewModels │ │ Services │ │ Views │ └───Orders │ │ Model │ │ Services │ │ ViewModels │ │ Services │ │ Views
なるほど.
このような構成にするメリットは,変更しようとするファイルがわかりやすい
,新規メンバーにもわかりやすい
というものがある.
かつ,Visual StudioにはソリューションエクスプローラにScope to This
という機能があり,これを使うと指定したフォルダ以下だけが表示されるが,この機能との相性がとても良い.
「Dashboard」フォルダにScope to Thisした後のソリューションエクスプローラ↓
ViewとViewModelをどのように登録するか
Prismの基本的な動作としては命名規則によってViewとViewModelが自動的に結び付けられる.
ただし,これはPrismの想定するフォルダ構成の場合だ.
今回はフォルダ構成を変更しているので自動的には結び付けられない.
手動
一つの方法は,ViewとViewModelを明示的に結びつける方法だ.
例えばApp.xaml.csファイル内のRegisterTypes
メソッドで以下のように記述します.
protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<SalesView, SalesViewModel>(); }
自動
Prismがどのように自動でViewとViewModelを結びつけているかというと,ViewModelLocaterによってこれを行っている.
デフォルトでは以下の規則に従ってViewとViewModelを結びつける.
- ViewとViewModelは同じアセンブリ内にあること
- ViewModelは
ViewModels
名前空間にあること - Viewは
Views
名前空間にあること - ViewModelの名前は
{View名}ViewModel
であること
フォルダ構成が異なる場合は上記の規則からはずれるため,Prismによって結びつけることができない.
このような場合はViewModelLocaterの設定を変更してやればよい.
ViewModelLocaterの設定変更
PrismApplicationクラスのConfigureViewModelLocator
メソッドをオーバーライドすることで変更することができる.
App.xaml.cs内のAppクラス内の該当箇所を以下のようにする.
ViewとViewModelを結びつける規則は以下のとおりとする.
View
PrismFeatureFolder.Features.Dashboard.Views.MainPage
と
ViewModel
PrismFeatureFolder.Features.Dashboard.ViewModels.MainPageViewModel
こんな対応関係で同じ機能フォルダ(ここではDashboardフォルダ)内のViewとViewModelを結びつけます.
ViewとViewModelの名前の規則は,View名〇〇
に対してViewModel名〇〇ViewModel
となるように設定します.
Viewの名前に対して,どういう規則でViewModelを対応付けるかを指定しているのがGetViewModelName
メソッドです.
このメソッドはViewの型(クラス)を受け取って,その名前を使って対応するViewModelを探して対応付けます.
protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); //We set here the type resolver ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(GetViewModelName); } private Type GetViewModelName(Type viewType) { //Viewに体操するViewModelの名前を生成 //viewType.FullNameで取得されるのは名前空間も含めた完全名. //その名前空間の「Views」の部分を「ViewModels」に置換している. var viewModelName = viewType.FullName.Replace("Views", "ViewModels")+"ViewModel"; var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; return Type.GetType($"{viewModelName}, {viewAssemblyName}"); }
実行
ViewとViewModelを追加し,AppクラスのRegisterTypes
メソッド内でViewを登録し,OnInitialized
メソッドで起動時に開くViewを指定して,実行します.
この時点でApp内はだいたい以下のようになっていると思います.
public partial class App { public App(IPlatformInitializer initializer=null):base(initializer) { } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<MainPage>();//規則に従ってViewModelと結び付けられる //containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();//明示的に指定 } protected override void OnInitialized() { InitializeComponent(); NavigationService.NavigateAsync(nameof(MainPage)); } protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); //We set here the type resolver ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(GetViewModelName); } private Type GetViewModelName(Type viewType) { var viewModelName = viewType.FullName.Replace("Views", "ViewModels")+"ViewModel"; var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; return Type.GetType($"{viewModelName}, {viewAssemblyName}"); } }
データバインディングは機能したでしょうか.
PrismによってViewとViewModelの対応付が行われるため,開発者はViewにもコードビハインドにも両者を結びつける記述をする必要がなく,互いに疎な構造にすることができます.
今回のソースコードは以下にあります.
Prismを使用する場合は,Prismテンプレートを使えるようにする拡張機能をインストールするか,もしくは手動で適用するか,どちらでも好きな方を選択できます.
手動で適用する場合は以下に記事を書きました.