WPFの初心者です。WPFのなんたるやも知らないまま、Prism & ReactivePropertyで MVVM に挑んでいるところですが・・・全然わかりません。まずはPrismのHello world的なサンプルを作ることにしました。
目標
PrismのRegionで、「Window(枠)は1つで内側の表示を切り替える」だけのサンプルアプリを作る。
ところが、Regionの使い方がわからない、ビルドが通らない、Viewを表示してもRegionに何も表示されない・・・なかなかにハマってしまいました。さんざん調べまくり、ようやく、参考になる記事と公式サンプルに・・・たどり着いた、というよりは、記事の使い方が分かった、という感じです。結果としてどうすればよかったのか、ほかの初心者の方の参考になれば幸いです。
- MainWindowに、Buttonを2つ、Region(ContentControl)を1つ配置。
- Buttonによって、Regionに表示されるView(UserControl)を切り替える。
- Regionに初期表示されるView(UserControl)を指定する。
Prismの公式サンプル
「17-BasicRegionNavigation」とやっていることは同じですが、Moduleを使わずに簡易化したものです。
Module化しないように作り変えるなんてMVVMの方向性に逆行しているのでしょうが、RegionManagerでNavigateすることだけを切り取るためにあえてそうしました。
環境
VisualStudio 2019
Prism Template Packインストール
Prism.Unity 7.2
プロジェクト作成
Prism Template Pack
Prismはライブラリでもあり、プロジェクト構築サポート機能??でもあるようです。
WPF(Prism)アプリ作成に先立ち、Prism Template Packをインストールしておきます。VisualStudio自体をグレードアップするようなイメージです。これにより、VisualStudioから Prismのプロジェクトテンプレートが使えるようになります。
VisualStudio MarketPlace
VS2017だとPrism公式サンプルが開けないようです。VS2019にしておきましょう。 (Prismのインストールがうまくいってなかっただけかも)
プロジェクト作成
VisualStudioのプロジェクト新規作成で、Prism Blank App (WPF)を選択。
DIコンテナにUnityを選択します。
一度ビルドすると、プロジェクトにPrism.Unityが組み込まれます(Prism Template Packのおかげらしい)。
公式サンプル「17-BasicRegionNavigation」では別プロジェクトにRegion埋め込み用のViewを作っていますが、ここでは簡略して同プロジェクトにします。
Regionに埋め込むView(UserControl)
プロジェクト内のフォルダ Views に対し、[追加]>[新しい項目の追加]で、Prism UserControlを選択します。クラス名(コントロール名)をViewAとします。同様にViewBを作成します。
Prismが、Viewに対応するViewModelのクラスの自動生成と、Viewのxamlにprism:ViewModelLocator.AutoWireViewModel="True"
の付け足しをしてくれます。
Viewの作成
ViewA,ViewBの内容
公式サンプル「17-BasicRegionNavigation」と同様、それぞれ単にViewA, ViewB と表示するTextBlockを一つあるだけとします。下記はViewAのxamlのサンプル。
<UserControlx:Class="BlankApp3.Views.ViewA"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"><Grid><TextBlockText="ViewA"FontSize="48"HorizontalAlignment="Center"VerticalAlignment="Center"/></Grid></UserControl>
このサンプルではViewModelでは何もしません。コードも自動生成のままでよいです。INavigationAwareを実装しなくてもとりあえずNavigationされるようです。
MainWindowのxaml
公式サンプル「17-BasicRegionNavigation」と同様、MainWindowに、Buttonを2つ、Regionを1つ配置します。ButtonによってRegionの内容をViewA⇔ViewB切り替えできるようにします。
自動生成されたMainWindowView.xamlに対し、Buttonを2つ追加します。ContentControlは大きさだけ調整します。
ButtonのCommandには、ViewModelに定義するNavigateCommandというプロパティをバインドします。さらにCommandParameterには、切り替えるViewのクラス名を指定します。ViewA.xamlならViewAです。
<Windowx:Class="BlankApp3.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="200.784"Width="334.091"><DockPanelLastChildFill="True"><StackPanelOrientation="Horizontal"DockPanel.Dock="Top"Margin="5"><ButtonCommand="{Binding NavigateCommand}"CommandParameter="ViewA"Margin="5">Navigate to View A</Button><ButtonCommand="{Binding NavigateCommand}"CommandParameter="ViewB"Margin="5">Navigate to View B</Button></StackPanel><ContentControlprism:RegionManager.RegionName="ContentRegion"Margin="5"/></DockPanel></Window>
RegionManagerとDI
【備考】Prism7.1 以降でアプリ起動時のコードが変更されています。過去の人気記事とは少し違うことに注意してください。
Region上のViewを切り替えることとDI(Dependency injection)とは関係ありませんが、Prismを使うにあたってDIコンテナの使用が前提となります。RegionManagerのクラス登録はPrismが作ってくれるらしく、自分のコード上では何も指定しません。一方で、自分で作るViewはクラス登録が必要です。Viewの切り替え(Navigation)に使えるように、DIコンテナに、ViewA, ViewBのクラスを登録します。※ViewA,ViewBのインスタンスの生成・破棄のタイミングは別途指定が必要になるはずです。未調査&割愛。
usingBlankApp3.Views;usingPrism.Ioc;usingPrism.Modularity;usingSystem.Windows;namespaceBlankApp3{/// <summary>/// Interaction logic for App.xaml/// </summary>publicpartialclassApp{protectedoverrideWindowCreateShell(){returnContainer.Resolve<MainWindow>();}protectedoverridevoidRegisterTypes(IContainerRegistrycontainerRegistry){containerRegistry.RegisterForNavigation<Views.ViewA>();containerRegistry.RegisterForNavigation<Views.ViewB>();}}}
ViewA, ViewBを切り替える仕組みとして、MainWindowViewModel内でRegionManagerを使います。そのRegionManagerはDIでもらいます。
ところで、PrismでViewModelがRegionManagerをDIコンテナからもらう(サンプルの)コードには2種類あります。プロパティでもらう場合と、コンストラクタでもらう場合です。
プロパティでもらうには、Prism.Unityとは別に、NuGetでUnity(作成者: Unity Container Project)をインストールします。[Dependency]を指定するにはusing Unity
が必要です。ちなみに過去の人気記事ではusing Microsoft.Practices.Unity
となっていますが、2016年頃にdllが変わったようです。
usingUnity;略namespaceBlankApp3.ViewModels{publicclassMainWindowViewModel:BindableBase{[Dependency]Prism.Regions.IRegionManagerRegionManager{get;}略
のようにします。ただし、この場合はMainWindowViewModelのコンストラクタの時点では RegionManagerプロパティがnullです。従ってコンストラクタでRegionの初期表示Viewを指定することはできません。インスタンスが作成された後、何かのイベントで初めて使えるようになるようです。この記事には、MainWindowの(コードビハインドの)Activateイベントで行う例が紹介されています。
もう一つの方法、コンストラクタでもらう場合には、NuGetでUnityを取得する必要はありません。このサンプルはこちらを使います。
publicclassMainWindowViewModel:BindableBase{privatereadonlyIRegionManager_regionManager;publicMainWindowViewModel(IRegionManagerregionManager){_regionManager=regionManager;
Regionに表示されるViewの切り替え(Navigation)と、初期表示指定
サンプルはMainWindow上のButtonでViewA, ViewBを切り替える、という仕様です。MainWindow.xamlの側ではButtonでCommand="{Binding NavigateCommand}"
としています。そのNavigateCommandを、MainWindowViewModelに定義します。さらにCommandParameterを受け渡すためにstringを引数としたCommandとしています。public DelegateCommand<string> NavigateCommand { get; private set; }
このDelegateCommandでNavigate()メソッドを呼び出し、RegionManagerに対しViewを切り替える要求を出します。
初期表示の指定にはRegisterViewWithRegion()を使えるようです。切り替えの要求(RequestNavigateメソッド)では、初期表示を指定できませんでした。
まとめて、MainWindowViewModel.csはこのようになります。
usingPrism.Mvvm;usingPrism.Regions;usingPrism.Commands;namespaceBlankApp3.ViewModels{publicclassMainWindowViewModel:BindableBase{privatestring_title="Prism Application";publicstringTitle{get{return_title;}set{SetProperty(ref_title,value);}}privatereadonlyIRegionManager_regionManager;publicDelegateCommand<string>NavigateCommand{get;privateset;}publicMainWindowViewModel(IRegionManagerregionManager){_regionManager=regionManager;_regionManager.RegisterViewWithRegion("ContentRegion",typeof(Views.ViewA));NavigateCommand=newDelegateCommand<string>(Navigate);}privatevoidNavigate(stringnavigatePath){if(navigatePath!=null)_regionManager.RequestNavigate("ContentRegion",navigatePath);}}}
参考
WPF PRISM 入門エントリまとめ
Prism公式サンプル
[WPF/C#]Prism(6.3.0)のRegionで画面遷移をする
Prism-Samples-Wpfの勉強メモ