かずきのBlog@hatena

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

Xamarin.FormsでPrism.Mvvmを使う

Prism.Mvvmは非常にシンプルなMVVMをサポートするライブラリです。Xamarin.Formsでも使わない手はない!ということで使ってみました。

環境設定

Windows Phoneのプロジェクトを消します。日本で出てないし、Prism.Mvvmサポートしてないプラットフォームなので泣く泣く…。この時点で私はAndroidしか試せないのでiOSも消してしまいました。

PCLのプロパティを開いてWindows Phoneのチェックを外しておきます。

NuGetでの導入

Prism.Mvvmで検索してPCLとAndroidのプロジェクト両方にPrism.Mvvmを追加します。

Viewの作成

Viewsという名前のフォルダを作ってForms Xaml Pageを作成します。

Prism.MvvmのVとVMの紐づけ機能を使うには、ViewがIViewインターフェースを実装してないといけないので、これを実装します。DataContextというプロパティが必要になるのでBindingContextをラップする形で実装します。ついでに、コンストラクタにViewModelLocationProviderのAutoWireViewModelChangedを呼び出しておいて、紐づけ機能を有効にしておきます。

using Microsoft.Practices.Prism.Mvvm;
using Xamarin.Forms;

namespace App8.Views
{
    public partial class MainPage : ContentPage, IView
    {
        public MainPage()
        {
            InitializeComponent();
            ViewModelLocationProvider.AutoWireViewModelChanged(this);
        }

        public object DataContext
        {
            get
            {
                return this.BindingContext;
            }
            set
            {
                this.BindingContext = value;
            }
        }
    }
}

ViewModelの作成

ViewModelsフォルダを作って、そこにViewのクラス名+ViewModelという命名規約でViewModelを作成します。この例ではMainPageViewModelですね。とりあえず、適当に。

using Microsoft.Practices.Prism.Mvvm;
using System.Windows.Input;
using Xamarin.Forms;

namespace App8.ViewModels
{
    public class MainPageViewModel : BindableBase
    {
        private string mainText;

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

        public ICommand HelloCommand { get; private set; }

        public MainPageViewModel()
        {
            this.MainText = "Hello world";

            this.HelloCommand = new Command(() => this.MainText = "こんにちは世界");
        }
    }
}

XAMLの作成

さて、Viewに戻ってViewModelに紐づくXAMLを書いていきます。といっても、デフォルトでMainTextにバインドされてるLabelが用意されてるので、ここではStackLayoutとButtonを追加しただけです。Xamarin Studioだと若干のインテリセンスが効くので精神衛生にいいですが、Visual Studioで書くと発狂しそうになります。ここらへん、そのうち出てくるであろうデザイナに期待したいところですね。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App8.Views.MainPage">
  <StackLayout>
      <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
    <Button Text="Hello" Command="{Binding HelloCommand}" />
  </StackLayout>
</ContentPage>

MainPageの設定

App.csのMainPageを、先ほど作成したMainPageクラスに差し替えます。

public App()
{
    // The root page of your application
    MainPage = new MainPage();
}

実行

実行すると以下のようになります。

f:id:okazuki:20150210214703p:plain

f:id:okazuki:20150210214727p:plain

Unity(DIコンテナのほう)との連携

Prism.MvvmのVとVMの連携は、UnityによるDIと連携するとうれしいと個人的に思ってます。UnityをプロジェクトにNuGetから追加します。

ついでに、Prism.PubSubEventsも追加しておきます。Prism.PubSubEventsのクラスをVMにDIしてみたいと思います。

App.csのコンストラクタでUnityの初期化とViewModelのインスタンス化の処理の書き換えを行います。

public App()
{
    var c = new UnityContainer();
    // EventAggregatorをシングルトンで管理してもらう
    c.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
    // ViewModelをUnityから取得するようにする
    ViewModelLocationProvider.SetDefaultViewModelFactory(t => c.Resolve(t));

    MainPage = new MainPage();
}

ViewModelのコンストラクタなどでIEventAggregatorを受け取るようにするとUnityが勝手にインスタンスを設定してくれます。

public MainPageViewModel(IEventAggregator eventAggregator)

Modelのルート要素をDIしてもらったり、設定情報を保持するクラスをDIしてもらったりとか色々便利です。オブジェクトのインスタンスの管理をお任せできるので個人的に気に入ってます。

まとめ

Prism.Mvvm + UnityはXamarin.Formsでも使える(ただしWindows Phoneは除く)