かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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