04-ViewDiscovery

Descriptionには Automatically inject views with View Discovery とあります。

やっと動きがありました。とはいえ、アプリケーションを動かしても「View A」と表示されるだけですけどね…

App.xaml

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

App.xaml.cs

using System.Windows;
using ViewDiscovery.Views;

namespace ViewDiscovery
{
    /// <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) { }
    }
}

MainWindow.xaml

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

MainWindow.xaml.cs

using System.Windows;

namespace ViewDiscovery.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow(IRegionManager regionManager)
        {
            InitializeComponent();
            //view discovery
            regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
        }
    }
}

ここで、ContentRegion にリージョン ViewA を割りあてているのでしょう。

…というか、この引数は CommunityToolkit.Mvvm におけるコンストラクタ注入のようですね?

ViewA.xaml

<UserControl
    x:Class="ViewDiscovery.Views.ViewA"
    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:ViewDiscovery.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="300"
    d:DesignWidth="300"
    mc:Ignorable="d">
    <Grid>
        <TextBlock FontSize="38" Text="View A" />
    </Grid>
</UserControl>

そして、MainWindow.xaml.cs 経由で、この ViewA.xaml にある「View A」を表示しています。実際に

<TextBlock FontSize="38" Text="View A" />

Text の内容を書き換えれば、アプリケーションでの表示が変わります。 このサンプルはこの程度でいいでしょう、次行きますよ次!

05-ViewInjection

Descriptionには Manually add and remove views using View Injection とあります。

ビルドして実行すると「Add View」ボタンがあり、これをクリックすると ViewA が表示されます。

App.xaml.cs と ViewA.xaml

前節と同じです。

App.xaml

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

MainWindow.xaml

<Window
    x:Class="ViewInjection.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"
    Width="525"
    Height="350">
    <DockPanel LastChildFill="True">
        <Button Click="Button_Click" DockPanel.Dock="Top">Add View</Button>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
    </DockPanel>
</Window>

MainWindow.xaml.cs

using System.Windows;

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

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

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var view = _container.Resolve<ViewA>();
            IRegion region = _regionManager.Regions["ContentRegion"];
            region.Add(view);
        }
    }
}

ここで、private readonly を追記しています。

やはり MainWindow はコンストラクタ注入のようですね?

ボタンがクリックされたら、ContentRegion リージョンにコンテナに ViewA を追加している、という処理に見えます…

まあ、考えていても分からない物は分からない!次行きますよ次!

06-ViewActivationDeactivation

Descriptionには Manually activate and deactivate views とあります。

ここに来て一気に動く物が来ました。

「Activate ViewA」「Activate ViewB」で ViewAViewB をアクティブにして 「Deactivate ViewA」「Deactivate ViewB」でアクティブにされている View を無効にしています。

ここできちんと動かせば分かるのですが 「Activate ViewA」→「Activate ViewB」→「Deactivate ViewB」としても、ViewAがアクティブになるわけではありません。

それでは、少し丁寧にソースを追いかけてみましょう。

App.xaml.cs と ViewA.xaml

前節と同じです。

ViewB.xaml

基本的に ViewA.xaml と同一で、以下の部分だけが異なっています。

<TextBlock FontSize="38" Text="View B" />

App.xaml

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

MainWindow.xaml

<Window
    x:Class="ActivationDeactivation.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"
    Width="525"
    Height="350">
    <DockPanel LastChildFill="True">
        <StackPanel>
            <Button Click="Button_Click" Content="Activate ViewA" />
            <Button Click="Button_Click_1" Content="Deactivate ViewA" />
            <Button Click="Button_Click_2" Content="Activate ViewB" />
            <Button Click="Button_Click_3" Content="Deactivate ViewB" />
        </StackPanel>
        <ContentControl
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            prism:RegionManager.RegionName="ContentRegion" />
    </DockPanel>
</Window>

MainWindow.xaml.cs

using System.Windows;

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

        ViewA _viewA;
        ViewB _viewB;

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

            this.Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            _viewA = _container.Resolve<ViewA>();
            _viewB = _container.Resolve<ViewB>();

            _region = _regionManager.Regions["ContentRegion"];

            _region.Add(_viewA);
            _region.Add(_viewB);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //activate view a
            _region.Activate(_viewA);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            //deactivate view a
            _region.Deactivate(_viewA);
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            //activate view b
            _region.Activate(_viewB);
        }

        private void Button_Click_3(object sender, RoutedEventArgs e)
        {
            //deactivate view b
            _region.Deactivate(_viewB);
        }
    }
}

いや、サンプルとは言えコードビハインドの命名が酷いですね…ああ、前回と同様に private readonly を追記しています。

肝となるのはこのコードですね。

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    _viewA = _container.Resolve<ViewA>();
    _viewB = _container.Resolve<ViewB>();

    _region = _regionManager.Regions["ContentRegion"];

    _region.Add(_viewA);
    _region.Add(_viewB);
}

こうすることで、_region には ViewAViewB の二つ View が入るわけですが、同時に使われる訳じゃなさそうです。

排他的に Activate() で有効化されて、有効化された View に対してだけ Deactivate() が実行される訳ですね。

で、Deactivate() は無効化されるだけで、_region では相変わらず使える訳です。

一旦締めます

とりあえず、region については正直「わかったような、わからないような…?」という印象が拭えません。

ざっくり「複数のViewを登録して、切り替えられる」という認識で良さそうですね。

致命的な誤りがあった場合、フィードバックをしていただけると幸いです。

次は、サンプルから一旦離れて、Prism を使った View のコードを書いてみます。

WPFにおけるPrism集中講座(3) 試しにViewのサンプルを書いてみる に続きます。