プロジェクトの下準備

まず、MainWindow からメニューで「設定」を開いて、設定画面から「設定終了」ボタンで MainWindow に戻るサンプルです。

新規ソリューションを「WPFアプリケーション」で作成します。.NET 9 を利用すれば良いでしょう。

ここでは SimplePrismViewSample という名前にしています。

そして、NuGet から Prism.Unity をインストールします。自分は Visual Studio の NuGet 管理から入れていますが、dotnet 等を扱える人に、改めての説明は不要でしょう。

App.xaml 周辺の設定

Views の設定

ソリューションエクスプローラで Views フォルダを作り、MainWindow.xaml を Views フォルダに移動します。

デフォルトのソリューション直下に MainWindow.xaml を置いていたら、以下の書き換えでエラーを吐きます。

…一体どこに Views が含まれてるんですかね?ブラックボックス臭い…

MainWindow.xaml の修正

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

MainWindow.xaml の位置を移動したので、x:Class="SimplePrismViewSample.MainWindow"x:Class="SimplePrismViewSample.Views.MainWindow" に書き換える必要があります。

App.xaml の記述

App.xaml を以下のように書き換えます。

<prism:PrismApplication
    x:Class="SimplePrismViewSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SimplePrismViewSample"
    xmlns:prism="http://prismlibrary.com/">
    <Application.Resources />
</prism:PrismApplication>

App.xaml.cs の記述

App.xaml.cs を以下のように書き換えます。

using System.Windows;
using SimplePrismViewSample.Views;

namespace SimplePrismViewSample
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}

明示的に using SimplePrismViewSample.Views; としなければエラーを吐きます。

View の作成

MainControl.xaml と SettingsControl.xaml をユーザーコントロールとして Views フォルダ内に作ります。

内容は以下の通りです。

MainControl.xaml

<UserControl
    x:Class="SimplePrismViewSample.Views.MainControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:SimplePrismViewSample.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <Grid>
        <Label Content="メイン画面です" FontSize="30" />
    </Grid>
</UserControl>

SettingsControl.xaml

<UserControl
    x:Class="SimplePrismViewSample.Views.SettingsControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:SimplePrismViewSample.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>

        <Label
            Grid.Row="0"
            Grid.Column="0"
            Content="設定画面です"
            FontSize="30" />
        <Button
            Grid.Row="1"
            Grid.Column="1"
            Margin="5"
            Content="設定終了"
            FontSize="16" />

    </Grid>
</UserControl>

MainWindow.xaml.cs の記述

using System.Windows;

namespace SimplePrismViewSample.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly IContainerExtension _container;
        private readonly IRegionManager _regionManager;
        private IRegion? _region;

        private MainControl? _mainControl;
        private SettingsControl? _settingsControl;

        public MainWindow(IContainerExtension container, IRegionManager regionManager)
        {
            InitializeComponent();
            _container = container;
            _regionManager = regionManager;

            Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            _mainControl = _container.Resolve<MainControl>();
            _settingsControl = _container.Resolve<SettingsControl>();

            _region = _regionManager.Regions["ContentRegion"];

            _region.Add(_mainControl);
            _region.Add(_settingsControl);

            _region.Activate(_mainControl);
        }

        private void MenuSettings_Click(object sender, RoutedEventArgs e)
        {
            _region?.Activate(_settingsControl);
        }

    }
}

メニューを MainWindow.xaml に追加します、Grid 内だけの記述にします。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Menu>
        <MenuItem Header="設定" Click="MenuSettings_Click"/>
    </Menu>
    <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>

現代では、null 非許容型に null を入れると警告がでますので、null 許容型にしています。

なお、_region への操作は、MainWindow が呼び出された後でなければ例外が発生するので、 サンプルと同様に Loaded イベントで処理をしています。

なぜ、Prism のサンプルプロジェクトで警告が出なかったかというと、.NET 6 を利用しているからです。 .NET 7 以降では、明示的に null 許容型にしないと警告が出ます、これは安全なコードのためです。

どうしても ? が気に食わない場合は .NET 6 以前を使ってくださいとしか言いようがありませんね…諦めましょう。

設定画面から戻る処理

まだメッセージングが使えないので、イベントを使って処理します。

SettingsControl.xaml

設定終了ボタン部分だけです。

<Button
    Grid.Row="1"
    Grid.Column="1"
    Margin="5"
    Click="SettingsExit_Click"
    Content="設定終了"
    FontSize="16" />

SettingsControl.xaml.cs

using System.Windows.Controls;

namespace SimplePrismViewSample.Views
{
    /// <summary>
    /// SettingsControl.xaml の相互作用ロジック
    /// </summary>
    public partial class SettingsControl : UserControl
    {
        public SettingsControl()
        {
            InitializeComponent();
        }

        public event EventHandler? SettingsExitEvent;

        private void SettingsExit_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            SettingsExitEvent?.Invoke(this, EventArgs.Empty);
        }
    }
}

MainWindow.xaml.cs

MainWindow_Loaded を以下のように書き換えます。

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    _mainControl = _container.Resolve<MainControl>();
    _settingsControl = _container.Resolve<SettingsControl>();
    _settingsControl.SettingsExitEvent += SettingsControl_SettingsExitEvent;

    _region = _regionManager.Regions["ContentRegion"];

    _region.Add(_mainControl);
    _region.Add(_settingsControl);

    _region.Activate(_mainControl);
}

また、イベントを捕捉するメソッドを作成します。

private void SettingsControl_SettingsExitEvent(object? sender, EventArgs e)
{
    _region?.Activate(_mainControl);
}

これで、メニューの「設定」を押したら設定画面に移行し「設定終了」を押したらメイン画面に移行するひな形が出来上がったことになります。

当然、このような設定画面は View だけで完結させていい話じゃありません、あくまで今までの Prism サンプルを踏まえて作ってみた「サンプル」に過ぎません。

このサンプルはもりゃきのGitHubに載せておきます。

個人的には自分で手を動かして理解して欲しいんですけどね…やっぱ完成形の提示は大切なのかなと。

次回からは、サンプルの読み解きの続きになります。

WPFにおけるPrism集中講座(4) ソリューション内のプロジェクトを利用する に続きます。