Overview
毎度毎度おまじないが多いので,ここできっちりまとめておきます。
MvvmCross v3.1.1時点ではXamarin.Mac向けにはNuSpecが書かれておらず,NuGet経由で組み込むことはできません。Xamarin.Mac+MvvmCrossでアプリ開発を始める準備の準備(2014年3月)を参考に,事前に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を参考に,リンカーの設定を変更しておいてください。
ここで一度実行し,空のWindowが表示されるのを確認しておくのをお勧めします。うまくいかないときは潔くXamMacプロジェクトをまるっと削除し,最初からやり直してください。
Setup.cs
Setup.csをプロジェクトルートに追加し,下記のように記述します。MyApp名前空間は適宜置き換えてください。
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にも記述が必要になります。
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
を追加します。名前はビューモデルに合わせてください。
FirstView.cs
することは多くありません。ベースクラスをMvxViewに変更します。
// (省略)
namespace MyApp.UI.Mac
{
public partial class OperationView : MvxView
{
#region Constructors
// (以下略)
FirstViewController.cs
こちらではベースクラスをMvxViewControllerに変更し,ViewModelプロパティを型強制しておきます。遺憾ながらWPFとは異なり,ViewModelに直接アクセスしないとならない場面が多々あります。
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
です。接続先に注意してください。
テキストボックスとラベルを配置し,HelloBox, HelloLabelと名前をつけました。
XcodeからXamarin Studioに切り替えてしばらくすると変更が同期されます。Xamarin Studioを複数起動しているとうまく同期できなくなるので注意してください。
Binding
FirstViewController.hの変更がうまく同期されたら,データを紐付けていきます。CoreプロジェクトのGreetingプロパティをテキストボックスとラベルにバインドし,即時変更されることを確かめましょう。
バインディングセットを記述するのはWindowDidLoad
オーバーライドメソッドです。
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
になっています。
Conclusion
紆余曲折を経てここにたどり着きました。NuGetでできればこんなことは悩まないですが,最低限動かすのに必要な設定を手で書けた方が理解も早いはずです。