WPF で Prism と Autofac を使う


WPF で Prism と  Autofac を使います。

# 開発環境
Visual Studio Community  2017
.Net Framework 4.6.2
Prism.Autofac 6.3.0

# 準備

Visual Studio で 新規作成から WPF アプリを選択してプロジェクトを作成します。本記事では プロジェクト名を EduPrismWpfApp としています。

## PrismTemplatePack

WPFに限らず Xamrin.Forms など Prism を使って開発をするときに、コントロールを専用のものにしたり、ViewModelLocator の設定など xaml に記述する必要がありますが、これらをテンプレート化してくれている Visual Studio の拡張機能として PrismTemplatePack がありますのでこれを入れておくと少し楽かもしれません。また、今回は使用していませんが Unity や Autofac 用のプロジェクトテンプレートも含まれています。

*PrismTemplatePack*
[https://marketplace.visualstudio.com/items?itemName=BrianLagunas.PrismTemplatePack](https://marketplace.visualstudio.com/items?itemName=BrianLagunas.PrismTemplatePack)

## パッケージの入手

『ソリューションの Nuget パッケージ管理』を開いて参照タブで Prism.Autofac と検索して一致したものをインストールしてください。 または『パッケージ マネージャーコントロール 』で以下のコマンドを入力してもインストールできます。

```bash
Install-Package Prism.Autofac -Version 6.3.0
```

*Prism.Autofac*
[https://www.nuget.org/packages/Prism.Autofac](https://www.nuget.org/packages/Prism.Autofac)

# Shell の作成

Views フォルダを作成してその中に Shell.xaml をウインドウとして作成します。

```xml:Shell.xaml
<Window x:Class="EduPrismWpfApp.Views.Shell"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             prism:ViewModelLocator.AutoWireViewModel="True" 
        Title="EduPrismWpfApp">
    <Grid>
            
    </Grid>
</Window>
```

初期状態でプロジェクト直下に自動生成されている MainWindow.xaml は削除しておきます。

#  Bootstrapper の作成

プロジェクト直下に Bootstrapper.cs を作成して `Bootstrapper` クラスに `AutofacBootstrapper` を継承させます。ここではShell の登録と表示をおこなうための処理を追加します。

```csharp
using Autofac;
using EduPrismWpfApp.Views;
using Prism.Autofac;
using System.Windows;

namespace EduPrismWpfApp
{
    class Bootstrapper : AutofacBootstrapper
    {
        protected override void ConfigureContainerBuilder(ContainerBuilder builder)
        {
            base.ConfigureContainerBuilder(builder);
            // Shell の登録
            builder.RegisterType<Shell>();
        }

        protected override DependencyObject CreateShell()
            => Container.Resolve<Shell>();

        protected override void InitializeShell()
        {
            base.InitializeShell();
            //  Shell を MainWindow に設定して表示する。
            Application.Current.MainWindow = (Shell)Shell;
            Application.Current.MainWindow.Show();
        }
    }
}
```

# App.xaml と App.xaml.cs の修正

作成した `Bootstrapper` をアプリケーション起動時に呼び出すように App.xaml と  App.xaml.cs を修正します。

```xml:App.xaml
<Application x:Class="EduPrismWpfApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:EduPrismWpfApp"
             Startup="Application_Startup">
    <Application.Resources>
         
    </Application.Resources>
</Application>
```

```csharp:App.xaml.cs
using System.Windows;

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

この状態で実行すると作成した Shell が起動することが確認できると思います。

# Module の登録

ソリューションに新しくプロジェクトを作成します。ソリューションエクスプローラでソリューションを右クリックして表示されるコンテクストメニューから「追加」→「新しいプロジェクト」を選択し『WPF ユーザーコントロールライブラリ』を選択してプロジェクトを作成します。本記事ではプロジェクト名を EduPrismWpfApp.Modules.SampleModule としています。普通のクラスライブラリではなく『WPF ユーザーコントロールライブラリ』で作成する理由としては参照設定など少し楽になるからです。作成されたプロジェクトに自動生成されているクラスまたはユーザーコントロールは削除しておきます。最初に作成したプロジェクトと同じように Nuget から Prism.Autofac のパッケージを追加しておいてください。

## Model の作成

プロジェクトに Models フォルダを作成してその中に SampleMessageProvider.cs を作成します。今回はサンプルとして、 単純にメッセージ文字列をプロパティとして持っているだけのインターフェイスと、そのインターフェイスを継承したクラスを用意します。

```csharp:SampleMessageProvider.cs
namespace EduPrismWpfApp.Modules.SampleModule.Models
{
    public interface ISampleMessageProvider
    {
        string Message { get; }
    }

    public class SampleMessageProvider : ISampleMessageProvider
    {
        public string Message => "Hello Prism App with Autofac";
    }
}
```

## ViewModel の作成

プロジェクトに ViewModels フォルダを作成してその中に SamplePageViewModel.cs を作成します。ViewModel ではコンストラクタの引数で先ほど作成した Model のインターフェイスを受取り、読み取り専用のプロパティとして View に公開するようにします。

```csharp:SamplePageViewModel.cs
using EduPrismWpfApp.Modules.SampleModule.Models;

namespace EduPrismWpfApp.Modules.SampleModule.ViewModels
{
    public class SamplePageViewModel
    {
        public ISampleMessageProvider SampleMessageProvider { get; }

        public SamplePageViewModel(ISampleMessageProvider sampleMessageProvider)
        {
            SampleMessageProvider = sampleMessageProvider;
        }
    }
}
```

## View の作成

プロジェクトに Views フォルダを作成してその中に SamplePage.xaml をユーザーコントロールとして作成します。View では ViewModel で公開されているプロパティからモデルに定義されているメッセージ文字列を `Label` 要素にバインドして表示するようにします。

```xml:SamplePage.xaml
<UserControl x:Class="EduPrismWpfApp.Modules.SampleModule.Views.SamplePage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Label Content="{Binding SampleMessageProvider.Message}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
    </Grid>
</UserControl>
```

## Module の作成

Prism 用と Autofac 用の二つの Module を作成します。まずは Prism 用の Module としてプロジェクトの直下に SampleModule.cs を作成します。このクラスに `Prism.Modularity.IModule` を継承させて `Initialize()` で 先ほど作成した View が初期表示されるように処理を追加します。また、画面遷移をおこなうためにコンストラクタの引数で `IRegionManager` を受取るようにしておきます。

```csharp:SampleModule.cs
using EduPrismWpfApp.Modules.SampleModule.Views;
using Prism.Modularity;
using Prism.Regions;

namespace EduPrismWpfApp.Modules.SampleModule
{
    /// <summary>
    /// Prism用
    /// </summary>
    public class SampleModule : IModule
    {
        private IRegionManager RegionManager { get; }

        public SampleModule(IRegionManager rm)
        {
            RegionManager = rm;
        }

        public void Initialize()
        {
            // MainRegion に SamplePage を表示
            RegionManager.RegisterViewWithRegion("MainRegion", typeof(SamplePage));
        }
    }
}
```

Autofac 用の Module としてプロジェクトの直下に SampleModuleRegistry.cs を作成します。このクラスに `Autofac.Module` を継承させて  `Load()` メソッドを `override` して Model と View を登録します。

```csharp:SampleModuleRegistry.cs
using Autofac;
using EduPrismWpfApp.Modules.SampleModule.Models;
using EduPrismWpfApp.Modules.SampleModule.Views;

namespace EduPrismWpfApp.Modules.SampleModule
{
    /// <summary>
    /// Autofac 用
    /// </summary>
    public class SampleModuleRegistry : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);

            // SampleMessageProvider を ISampleMessageProvider として登録
            builder.RegisterType<SampleMessageProvider>().As<ISampleMessageProvider>();
            // SamplePage を登録
            builder.RegisterType<SamplePage>();
        }
    }
}
```

##  Shell.xaml とBootstrapper.cs の修正

Shell.xaml に `ContentControl` 要素を追加して、 Region 名を指定します。リージョン名の指定は `prism:RegionManager.RegionName` に 文字列を設定することでおこなえます。

```xml:Shell.xaml
<Window x:Class="EduPrismWpfApp.Views.Shell"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             prism:ViewModelLocator.AutoWireViewModel="True" 
        Title="EduPrismWpfApp">
    <ContentControl prism:RegionManager.RegionName="MainRegion"/>
</Window>
```

Bootstrapper クラスに先ほど作成した Module を登録する処理を追加します。追加する内容は以下となります。

* `ConfigureContainerBuilder()` に Autofac 用に作成した Module を登録する処理を追加
*  `ConfigureModuleCatalog()` を `override` して Prism 用に作成した Module を登録する処理を追加

```csharp:Bootstrapper.cs
using Autofac;
using EduPrismWpfApp.Views;
using Prism.Autofac;
using Prism.Modularity;
using System.Windows;

namespace EduPrismWpfApp
{
    class Bootstrapper : AutofacBootstrapper
    {
        protected override void ConfigureContainerBuilder(ContainerBuilder builder)
        {
            base.ConfigureContainerBuilder(builder);

            // Shell の登録
            builder.RegisterType<Shell>();

            // Autofac Module の登録
            builder.RegisterModule<Modules.SampleModule.SampleModuleRegistry>();
        }

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

            // Prism Module の登録
            var mc = (ModuleCatalog)ModuleCatalog;
            mc.AddModule(typeof(Modules.SampleModule.SampleModule));
        }

        protected override DependencyObject CreateShell()
            => Container.Resolve<Shell>();

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

            //  Shell を MainWindow に設定して表示
            Application.Current.MainWindow = (Shell)Shell;
            Application.Current.MainWindow.Show();
        }
    }
}
```

この状態で実行すると View が表示されて Model に定義されているメッセージが表示されるのが確認できると思います。