かずきのBlog@hatena

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

WPFでPrismをライトウェイトに使いたい「Hello world」

WPF版Prismは使いこなすと強力です。でも使いこなすの大変です。ハイ。学習コストかけてられないし、学習コストかけたからといって1個のアプリ開発で、そのコストを回収できるかもわかりませんですしね。 ということで、なるべくライトにPrismを使ってみたいと思います。

Prismで使いたい機能

以下の機能を使おうと思います。

  • MVVM基本クラス
    • BindableBaseクラス
    • DelegateCommandクラス
  • ViewModelLocator
  • InteractionRequest
    • PopupWindowAction
  • DIコンテナのUnity

逆に以下の機能は使わない前提です。

  • Bootstrapper
  • Module
  • Region

では行ってみましょう。

プロジェクトの作成

LightweightPrismSampleという名前でプロジェクトを作ったという前提で説明します。NuGetからPrism.Unityをインストールします。

MainWindowは後で作り直すので削除しておきます。

Appクラス

App.xamlからStartupUriを消してStartupメソッドに書き換えます。ここでちょっとUnityの初期化とか書くようにします。

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

ViewModelLocatorを使うためのおまじないみたいなものです。アプリで管理しているUnityのコンテナからViewModelを生成するようにしています。

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

namespace LightweightPrismSample
{
    public partial class App : Application
    {
        // アプリで管理するコンテナ
        private IUnityContainer Container { get; } = new UnityContainer();

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            ViewModelLocationProvider.SetDefaultViewModelFactory(x => this.Container.Resolve(x));
        }
    }
}

Viewの作成

Views名前空間にMainWindowを作成します。ViewModelLocatorを使うおまじないをAppクラスでしてるので、ViewModelLocatorが使えます。

<Window x:Class="LightweightPrismSample.Views.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LightweightPrismSample.Views"
        xmlns:Prism="http://prismlibrary.com/"
        Prism:ViewModelLocator.AutoWireViewModel="True"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="300"
        Width="300">
    <Grid>
    </Grid>
</Window>

Appクラス再び

MainWindowを表示させます。せっかくなのでUnityから作りましょう(深い意味はない。DIしたければできるようになるっていうくらい)

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

namespace LightweightPrismSample
{
    public partial class App : Application
    {
        // アプリで管理するコンテナ
        private IUnityContainer Container { get; } = new UnityContainer();

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            ViewModelLocationProvider.SetDefaultViewModelFactory(x => this.Container.Resolve(x));
            this.Container.Resolve<MainWindow>().Show();
        }
    }
}

この時点で、MainWindowが表示されるようになります。

f:id:okazuki:20160716220307p:plain

ViewModelの作成

では、ViewModelを作成していきます。ViewModels名前空間に以下のようなViewModelを作ります。InteractionRequestやDelegateCommandやBindableBaseを使います。

using Prism.Commands;
using Prism.Interactivity.InteractionRequest;
using Prism.Mvvm;

namespace LightweightPrismSample.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public DelegateCommand AlertCommand { get; }

        public InteractionRequest<INotification> AlertRequest { get; } = new InteractionRequest<INotification>();

        private string input;

        public string Input
        {
            get { return this.input; }
            set { this.SetProperty(ref this.input, value); }
        }

        public MainWindowViewModel()
        {
            this.AlertCommand = new DelegateCommand(() =>
                this.AlertRequest.Raise(new Notification { Title = "Alert", Content = this.Input }));
        }
    }
}

Viewの調整

ViewModelに合わせてViewも調整します。InteractionRequestに応答するトリガーや、PopupWindowActionなんかを使っています。Behaviorを使うのでSystem.Windows.Interactionアセンブリを参照に追加するのを忘れずに。

<Window 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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LightweightPrismSample.Views"
        xmlns:Prism="http://prismlibrary.com/"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        x:Class="LightweightPrismSample.Views.MainWindow"
        Prism:ViewModelLocator.AutoWireViewModel="True"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="300"
        Width="300">
    <i:Interaction.Triggers>
        <Prism:InteractionRequestTrigger SourceObject="{Binding AlertRequest}">
            <Prism:PopupWindowAction />
        </Prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
    <StackPanel>
        <TextBox Text="{Binding Input, Mode=TwoWay}" />
        <Button Content="Alert"
                Command="{Binding AlertCommand}" />
    </StackPanel>
</Window>

実行

実行するとWindowが立ち上がり、TextBoxに入力した内容がボタンを押すとポップアップで表示されます。 画面遷移とかしたかったら自力でファイト!という感じで凝ったことしようとするとPrismの機能が恋しくなるかもしれませんが、そうじゃない軽いアプリならこんな構成もありかなという感じです。

f:id:okazuki:20160716221254p:plain

おまけ

今回のプロジェクトはGitHubに公開しています。

github.com

このプロジェクトを育てるには

AppクラスのStartupイベントでUnityのコンテナにModelやら必要なクラスの登録処理を書いたり色々やってよしなにやる感じです。