Using MvvmCross in Xamarin.Mac

##Overview
毎度毎度おまじないが多いので，ここできっちりまとめておきます。
MvvmCross v3.1.1時点ではXamarin.Mac向けにはNuSpecが書かれておらず，NuGet経由で組み込むことはできません。[Xamarin.Mac+MvvmCrossでアプリ開発を始める準備の準備（2014年3月）](http://qiita.com/xxxwing0/items/59c005080be04305e284)を参考に，事前にXamarin.Mac向けライブラリを準備してください。

##Project
　Coreプロジェクトは省略します。PCLでもNonPCLでもかまいません。
　Xamarin.Macプロジェクトを作成しますが，Xamain Studio 4.2.3時点ではソリューションにいきなりXamarin.Macプロジェクトを追加するとXamMacへの参照ができずにMonoMacネームスペースを解決できなくなりますので， **MonoMacプロジェクトを作ってMigrateする** 必要があります（新規ソリューションと合わせ技なら大丈夫みたいです）。
　Coreプロジェクトと，以下のライブラリ参照を追加しておきます。

* Cirrious.CrossCore
* Cirrious.CrossCore.Mac
* Cirrious.MvvmCross.Binding
* Cirrious.MvvmCross.Binding.Mac
* Cirrious.MvvmCross
* Cirrious.MvvmCross.Localization
* Cirrious.MvvmCross.Mac
* System.Windows

　さらにターゲットフレームワークを`Mono/.NET 4.5`に設定します。また[Xamarin.MacでMvvmCrossを使う際のWorkaround #1](http://qiita.com/xxxwing0/items/120be1e10f860b1a7124)を参考に，リンカーの設定を変更しておいてください。
　ここで一度実行し，空のWindowが表示されるのを確認しておくのをお勧めします。うまくいかないときは潔くXamMacプロジェクトをまるっと削除し，最初からやり直してください。

## Setup.cs
　Setup.csをプロジェクトルートに追加し，下記のように記述します。MyApp名前空間は適宜置き換えてください。

```csharp:Setup.cs
using System;
using Cirrious.MvvmCross.Mac.Platform;
using Cirrious.MvvmCross.Mac.Views.Presenters;

namespace MyApp.UI.Mac
{
	public class Setup: MvxMacSetup
	{
		public Setup (MvxApplicationDelegate applicationDelegate, IMvxMacViewPresenter presenter)
            : base (applicationDelegate, presenter)
        {
        }

        protected override IMvxApplication CreateApp()
        {
            return new MyApp.Core.App();
        }
	}
}
```

## AppDelegate.cs
　最初から作られているAppDelegateにも記述が必要になります。

```csharp:AppDelegate.cs
using MonoMac.Foundation;
using MonoMac.AppKit;
using Cirrious.MvvmCross.Mac.Views.Presenters;
using Cirrious.CrossCore;
using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.Mac.Platform;

namespace MyApp.UI.Mac
{
    public partial class AppDelegate : MvxApplicationDelegate
    {
        MainWindowController mainWindowController;

        public AppDelegate()
        {
        }

        public override void FinishedLaunching(NSObject notification)
        {
            mainWindowController = new MainWindowController();

            var presenter = new MvxMacViewPresenter(this, mainWindowController.Window);
            var setup = new Setup(this, presenter);
            setup.Initialize();

            var startup = Mvx.Resolve<IMvxAppStart>();
            startup.Start();

            mainWindowController.Window.MakeKeyAndOrderFront(this);
        }

        public override bool ApplicationShouldTerminateAfterLastWindowClosed(NSApplication sender)
        {
            return true;
        }
    }
}
```
もちろん`ApplicationShouldTerminateAfterLastWindowClosed`オーバーライドは任意です。

##Views
 プロジェクトに`Views`フォルダを追加し，`Cocoa View with Controller`を追加します。名前はビューモデルに合わせてください。
![kobito.1394170307.530577.png](https://qiita-image-store.s3.amazonaws.com/0/34563/b8710cb3-7609-e191-ea13-7b1b912e1727.png "kobito.1394170307.530577.png")

###FirstView.cs
　することは多くありません。ベースクラスをMvxViewに変更します。

```csharp:FirstView.cs
// （省略）
namespace MyApp.UI.Mac
{
    public partial class OperationView : MvxView
    {
        #region Constructors
		// （以下略）
```

###FirstViewController.cs
　こちらではベースクラスをMvxViewControllerに変更し，ViewModelプロパティを型強制しておきます。遺憾ながらWPFとは異なり，ViewModelに直接アクセスしないとならない場面が多々あります。

```csharp:FirstViewController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using MonoMac.Foundation;
using MonoMac.AppKit;
using Cirrious.MvvmCross.Mac.Views;
using MyApp.Core.ViewModels;

namespace MyApp.UI.Mac
{
    public partial class FirstViewController : MvxViewController
    {
		// Constructorは省略します
        #region Constructors

        //strongly typed view accessor
        public new FirstView View
        {
            get
            {
                return (FirstView)base.View;
            }
        }

        public new FirstViewModel ViewModel
        {
            get
            {
                return (FirstViewModel)base.ViewModel;
            }
        }
    }
}
```

###FirstView.xib
　Xcodeを使って適当にコントロールを配置し，アウトレットを接続します。接続するのは`FirstViewController.h`です。接続先に注意してください。
![Screen Shot 2014-03-07 at 2.41.13 PM.png](https://qiita-image-store.s3.amazonaws.com/0/34563/6d80f7a3-b0b0-1bd6-8cc1-eb7f87a1f0d9.png "Screen Shot 2014-03-07 at 2.41.13 PM.png")
　テキストボックスとラベルを配置し，HelloBox, HelloLabelと名前をつけました。
　XcodeからXamarin Studioに切り替えてしばらくすると変更が同期されます。Xamarin Studioを複数起動しているとうまく同期できなくなるので注意してください。

##Binding
　FirstViewController.hの変更がうまく同期されたら，データを紐付けていきます。CoreプロジェクトのGreetingプロパティをテキストボックスとラベルにバインドし，即時変更されることを確かめましょう。
　バインディングセットを記述するのは`WindowDidLoad`オーバーライドメソッドです。

```csharp:FirstViewController.cs
using Cirrious.MvvmCross.Binding.BindingContext;

public override void ViewDidLoad ()
{
	base.ViewDidLoad();

	var set = this.CreateBindingSet<FirstViewController, FirstViewModel>();
	set.Bind(HelloBox)
		.For("StringValue")
		.To(vm => vm.Greeting);
	set.Bind(HelloLabel)
		.For(c => c.StringValue)
		.To(vm => vm.Greeting)
		.OneWay();
	set.Apply();
}
```
　`set.Bind(コントロール）.For(バインド先プロパティ).To（ビューモデル側プロパティ）;` が基本です。最後に`Apply`を必ず呼んでください。バインド先プロパティの指定は式木でも文字列でもかまいません。OneWayなどもデフォルトでよければ指定の必要はありません。ValueConverterをかます場合は`.WithConversion(コンバータ名)`で指定します。
　ここまでできたら実行します。うまくいっていればテキストボックスに入力した値が即時反映されるはずです。MacのNSTextFieldは`UpdateSourceTrigger`が`OnPropertyChange`になっています。
![Screen Shot 2014-03-07 at 2.49.53 PM.png](https://qiita-image-store.s3.amazonaws.com/0/34563/713df893-5e8a-8ae1-a7ed-36380590083f.png "Screen Shot 2014-03-07 at 2.49.53 PM.png")

##Conclusion
　紆余曲折を経てここにたどり着きました。NuGetでできればこんなことは悩まないですが，最低限動かすのに必要な設定を手で書けた方が理解も早いはずです。