かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

PrismのRegionをなるべく仰々しくない方法で使う

Prismの全機能を使おうと思ったらModule使ったりとか結構だるいです。でも「Regionで画面区切って画面遷移できる機能とかは使いたい。」と思いました。

思考錯誤して出来たのでメモっておきます。

NuGetからのインストール

Prism.UnityExtensionsをNuGetからダウンロードします。

Shellの作成

MainWindowをさくっと消して、Viewsフォルダをきって、そこにShellという名前のWindowを作ります。そして中身にRegionを定義します。

<Window x:Class="WpfApplication11.Views.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://www.codeplex.com/prism"
        Title="Shell" Height="300" Width="300">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="Main" />
    </Grid>
</Window>

ViewModelLocatorを使ってViewModelを差し込みたいので、IViewを実装しておきます。

using Microsoft.Practices.Prism.Mvvm;
using System.Windows;

namespace WpfApplication11.Views
{
    /// <summary>
    /// Shell.xaml の相互作用ロジック
    /// </summary>
    public partial class Shell : Window, IView
    {
        public Shell()
        {
            InitializeComponent();
        }
    }
}

Bootstrapperの作成

Bootstrapperを作ります。ポイントは、ShellにViewModelを差し込むためのViewModelLocatorの設定をInitializeShellでやってるところです。ここでやらないと、RegionManagerにRegionが生成される前にViewModelが生成されたりして厄介なのです…。

あと、Views名前空間にあるViewを全てobject型としてコンテナに登録しておきます。これはRegionManager内で使うためです。

最後に、ViewModelLocationProviderに、ViewModelの型の生成をUnityで行うように生成処理を差し替えています。

using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Unity;
using System.Linq;
using System.Windows;
using WpfApplication11.Views;

namespace WpfApplication11
{
    public class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return this.Container.Resolve<Shell>();
        }

        protected override void InitializeShell()
        {
            ViewModelLocator.SetAutoWireViewModel(this.Shell, true); // ここでViewModelを差し込む
            ((Shell)this.Shell).Show();
        }

        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();

            // Viewを全てobject型としてコンテナに登録しておく(RegionManagerで使うため)
            this.Container.RegisterTypes(
                AllClasses.FromLoadedAssemblies().Where(t => t.Namespace.EndsWith(".Views")),
                getFromTypes: _ => new[] { typeof(object) },
                getName: WithName.TypeName);

            // ViewModelLocatorでViewModelを生成する方法をUnityで行うようにする
            ViewModelLocationProvider.SetDefaultViewModelFactory(t => this.Container.Resolve(t));
        }
    }
}

Regionに表示するViewを作成

Views名前空間にUserControlでMainPageを作ります。中身はとりあえず表示されればいいので適当です。

<UserControl x:Class="WpfApplication11.Views.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock Text="MainPage" />
    </Grid>
</UserControl>

本番では、こいつにViewModelLocatorを使ってVMを差し込んで使いますが、ここでは表示されることを確認したいだけなので、やってません。

ShellViewModelの作成

ShellのViewModelを作ります。コンストラクタでIRegionManagerを受け取って、MainPageを表示するように指示しています。

using Microsoft.Practices.Prism.Regions;

namespace WpfApplication11.ViewModels
{
    public class ShellViewModel
    {
        public ShellViewModel(IRegionManager rm)
        {
            rm.RequestNavigate("Main", "MainPage");
        }
    }
}

Startup処理を作成

App.xamlのStartupUriを消してStartupイベントを書きます。イベント内ではBootstrapperを作ってRunしてるだけです。

using System.Windows;

namespace WpfApplication11
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            var b = new Bootstrapper();
            b.Run();
        }
    }
}

実行

実行するとMainPageが画面に表示されます。Module機能使わないでRegionを使うことが出来ました。めでたしめでたし。

f:id:okazuki:20140911224640p:plain