redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

Prismを使用してサブウィンドウでRegionを使用した場合に発生したエラーと対処方法・その1

Prismを使用したWPFアプリケーションを開発していて、サブウィンドウでRegionを使用した場合に発生したエラーと対処方法

次の画面に遷移できない

現象

Regionを使用した画面遷移

ウィンドウのXAMLにRegionを設定し、コードビハインドで、IRegionManagerインターフェースのRegisterViewWithRegionメソッドを呼び出して、ユーザーコントロールとRegionを紐づける。

XAML

<Window x:Class="Regions.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/"
        Title="Shell" Height="350" Width="525">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>

コードビハインド

using System.Windows;

namespace Regions.Views
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
        }
    }
}

実行すると、ウィンドウにはユーザーコントロールの内容が表示される。

ユーザーコントロール上にあるボタンをクリックして、次の画面に遷移をしたいとする。Regionに設定されているユーザーコントロールを別のものに置き換えることで、画面遷移したように見せることが出来る。

具体的には、DIの設定時にRegisterForNavigationメソッドを使用してユーザーコントロールを登録しておき、ViewModelでIRegionManagerのRequestNavigateメソッドにRegionNameと表示したいユーザーコントロールを指定することで置き換えることができる。

App.xaml.cs(DI部分)

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<ViewB>();
}

XAML(遷移ボタン)

<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="5" >
    <Button Command="{Binding NavigateCommand}" CommandParameter="ViewB" Margin="5">Navigate to View B</Button>
</StackPanel>

ViewModel(遷移処理)

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

public MainWindowViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;

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

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

これはMainWindowでやると問題なく成功する(上記サンプルの詳細はこちらを参照)。

サブウィンドウでRegionを使用する

ある画面をサブウィンドウとして開くために、親ウィンドウから、サブウィンドウのShowメソッドもしくは、ShowDialogメソッドを呼び出した。

var subWindow = _container.Resolve<SubWindow>();
subWindow.ShowDialog();

サブウィンドウでも画面の切り替えをしたいので、サブウィンドウのXAMLにRegionを設定し、同様のコードを書いたが、実行すると次の画面に遷移できない(ユーザーコントロールが切り替わらない)。

対処方法

デバッグ実行でRegionManagerに登録されているRegionを調べてみると、サブウィンドウで指定したRegionNameが存在しない。 MainWindow(親ウィンドウ)でもRegionを設定して試してみると、MainWindowのRegionはあった。

ここから、ViewModelのDIで取得できるのは、MainWindowと関連付けられたRegionManagerであることがわかった。

サブウィンドウのRegionが、MainWindowのRegionManagerに登録できていない。登録がないため、RegionNameを指定しても画面遷移ができない。

調べてみると、サブウィンドウのXAMLで、RegionNameだけではなく、RegionManagerも指定出来ることがわかった。

ViewModelでDIした(MainWindowの)RegionManagerを、サブウィンドウのXAMLでRegionManagerに指定することで、 サブウィンドウでもMainWindowのRegionManagerを使用する事ができた。*1

XAML

<ContentControl prism:RegionManager.RegionName="SubContentRegion" prism:RegionManager.RegionManager="{Binding MainRegionManager}"/>

ViewModel

public IRegionManager MainRegionManager { get; }

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

public SubWindowViewModel(IRegionManager regionManager)
{
    MainRegionManager = regionManager;
    
    NavigateCommand = new DelegateCommand<string>(Navigate);
}

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

これでRegionNameを分けることで、サブウィンドウでもRegionを使用した画面遷移が出来るようになった。

動作環境

  • .NET Core 3.1
  • Prism 7.2

以上

*1:IRegionManagerインターフェースのCreateRegionManagerメソッドを呼び出すことで、新しいRegionManagerを取得する事もできる