LoginSignup
11
18

More than 3 years have passed since last update.

Prism.Wpf 7.2 Regionへの初期表示指定と画面遷移をしてみる

Last updated at Posted at 2020-03-20

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を作成します。
image.png
image.png

Prismが、Viewに対応するViewModelのクラスの自動生成と、Viewのxamlにprism:ViewModelLocator.AutoWireViewModel="True"の付け足しをしてくれます。

Viewの作成

ViewA,ViewBの内容

公式サンプル「17-BasicRegionNavigation」と同様、それぞれ単にViewA, ViewB と表示するTextBlockを一つあるだけとします。下記はViewAのxamlのサンプル。

ViewA.xaml
<UserControl x: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>
        <TextBlock Text="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です。

MainWindow.xaml
<Window x: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">
    <DockPanel LastChildFill="True">
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="5" >
            <Button Command="{Binding NavigateCommand}" CommandParameter="ViewA" Margin="5">Navigate to View A</Button>
            <Button Command="{Binding NavigateCommand}" CommandParameter="ViewB" Margin="5">Navigate to View B</Button>
        </StackPanel>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" Margin="5"  />
    </DockPanel>
</Window>

image.png
ViewModelのコード変更は後述します。

RegionManagerとDI

【備考】Prism7.1 以降でアプリ起動時のコードが変更されています。過去の人気記事とは少し違うことに注意してください。

Region上のViewを切り替えることとDI(Dependency injection)とは関係ありませんが、Prismを使うにあたってDIコンテナの使用が前提となります。RegionManagerのクラス登録はPrismが作ってくれるらしく、自分のコード上では何も指定しません。一方で、自分で作るViewはクラス登録が必要です。Viewの切り替え(Navigation)に使えるように、DIコンテナに、ViewA, ViewBのクラスを登録します。※ViewA,ViewBのインスタンスの生成・破棄のタイミングは別途指定が必要になるはずです。未調査&割愛。

App.xaml.cs
using BlankApp3.Views;
using Prism.Ioc;
using Prism.Modularity;
using System.Windows;

namespace BlankApp3
{
    /// <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)
        {
            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が変わったようです。
image.png

using Unity;

namespace BlankApp3.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        [Dependency]
        Prism.Regions.IRegionManager RegionManager { get; }

のようにします。ただし、この場合はMainWindowViewModelのコンストラクタの時点では RegionManagerプロパティがnullです。従ってコンストラクタでRegionの初期表示Viewを指定することはできません。インスタンスが作成された後、何かのイベントで初めて使えるようになるようです。この記事には、MainWindowの(コードビハインドの)Activateイベントで行う例が紹介されています。

もう一つの方法、コンストラクタでもらう場合には、NuGetでUnityを取得する必要はありません。このサンプルはこちらを使います。

    public class MainWindowViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;

        public MainWindowViewModel(IRegionManager regionManager)
        {
            _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はこのようになります。

MainWindowViewModel.cs
using Prism.Mvvm;
using Prism.Regions;
using Prism.Commands;

namespace BlankApp3.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        private readonly IRegionManager _regionManager;

        public DelegateCommand<string> NavigateCommand { get; private set; }

        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            _regionManager.RegisterViewWithRegion("ContentRegion", typeof(Views.ViewA));

            NavigateCommand = new DelegateCommand<string>(Navigate);
        }

        private void Navigate(string navigatePath)
        {
            if (navigatePath != null)
                _regionManager.RequestNavigate("ContentRegion", navigatePath);
        }
    }
}

参考

WPF PRISM 入門エントリまとめ
Prism公式サンプル
[WPF/C#]Prism(6.3.0)のRegionで画面遷移をする
Prism-Samples-Wpfの勉強メモ

11
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
18