Xamarin.iOS+MvvmCrossでstoryboardを使う方法(MvvmCross v3.2.2 対応)

Xamarin.iOS+MvvmCrossでstoryboardを使う方法 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5814

以前、書いたのですが、バージョンが上がって微妙に動きが異なっているので、メモ書きしておきます。
サンプルコードは http://1drv.ms/1CeHWYp からダウンロードができます。

MvvmCross のサンプルコードを storyboard 対応にする

Tip Calc The Core Project
https://github.com/MvvmCross/MvvmCross/wiki/Tip-Calc—The-Core-Project
Tip Calc A Xamarin.iOS UI project ・ MvvmCross/MvvmCross Wiki
https://github.com/MvvmCross/MvvmCross/wiki/Tip-Calc-A-Xamarin.iOS-UI-project

TipCalc.Core の中身

ViewModel は MvxViewModel クラスを継承して作ります。ここのバインドは、INotiryPropertyChanged を実装していればよいので、何で作っても良いはずです。
プロパティの変更時に、RaisePropertyChanged で変更通知をします。

public class TipViewModel : MvxViewModel
{
	private readonly ICalculation _calculation;
	public TipViewModel(ICalculation calculation)
	{
		_calculation = calculation;
	}

	public override void Start()
	{
		_subTotal = 100;
		_generosity = 10;
		Recalcuate();
		base.Start();
	}

	private double _subTotal;

	public double SubTotal
	{
		get { return _subTotal; }
		set { _subTotal = value; RaisePropertyChanged(() => SubTotal); Recalcuate(); }
	}

App.cs の中身は、空っぽでも動きます…と思ったけど、Calculation のインスタンスだけ作らないとダメです。この部分は、Calc 部分を ICalc で作っているので、こうなっているので、特に作らなければ App() の中身は空でも大丈夫です。

public App()
{
	Mvx.RegisterType<ICalculation, Calculation>();
	// Mvx.RegisterSingleton<IMvxAppStart>(new MvxAppStart<TipViewModel>());
}

RegisterSingleton を使うとシングルトンで作られてしまい、都合が悪いことが多いので、UI プロジェクトのほうで作ります。

TipCalc.UI.Touch の中身

Setup.cs の中身はほとんど空っぽです。

public class Setup : MvxTouchSetup
{
	public Setup(MvxApplicationDelegate appDelegate, IMvxTouchViewPresenter presenter)
		: base(appDelegate, presenter)
	{
	}

	protected override Cirrious.MvvmCross.ViewModels.IMvxApplication CreateApp()
	{
		return new App();
	}
}

AppDelegate.cs の中身は、Window プロパティを残して、FinishedLaunching だけオーバーライドします。
内部で window を作っているサンプルコードもあるのですが、別のところから Window プロパティを参照することができるので、元の状態で残したほうがベターです。
以前と違って、Setup の引数が変わっているので、MvxTouchViewPresenter オブジェクトを作って引き渡します。
まあ、Setup 自体を変えてしまってもいいのですが。

 [Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
	// class-level declarations
	public override UIWindow Window
	{
		get;
		set;
	}

	public override bool FinishedLaunching(UIApplication app, NSDictionary options)
	{
		var presenter = new MvxTouchViewPresenter(this, Window);
		var setup = new Setup(this, presenter);
		setup.Initialize();
		return true;
	}
}

TipCalc.UI.TouchViewController.cs の ViewModel は、MvxViewController を継承するように変更して、ViewDidLoad メソッドの中身だけ変更します。
base.ViewDidLoad で元のメソッドを呼び出す前に、MvxViewModelRequest オブジェクトを作らないといけません。この引数が変更になっていて、ViewModel のクラス名(ここでは TipViewModel )を渡します。

CreateBindingSet は拡張メソッドなので、
using Cirrious.MvvmCross.Binding.BindingContext;
することを忘れずに…いつもあれ?ってなるので。

public partial class TipCalcUITouchViewController : MvxViewController
{
	public TipCalcUITouchViewController(IntPtr handle)
		: base(handle)
	{
	}

	public new TipViewModel ViewModel
	{
		get { return (TipViewModel)base.ViewModel; }
		set { base.ViewModel = value; }
	}

	public override void ViewDidLoad()
	{
		this.Request = new MvxViewModelRequest(typeof(TipViewModel), null, null, new MvxRequestedBy());
		base.ViewDidLoad();
		// Perform any additional setup after loading the view, typically from a nib.
		var set = this.CreateBindingSet<TipCalcUITouchViewController, TipViewModel>();
		set.Bind(labelTip).To(vm => vm.Tip);
		set.Bind(textSubTotal).To(vm => vm.SubTotal);
		set.Bind(sliderGene).To(vm => vm.Generosity);
		set.Apply();
	}
}

Bind でコントロールを指定して、To で ViewModel のプロパティと結びつけます。いわゆる Binding の Path は .For( c => c.Text) で明示的に指定もできますが省略可能です(デフォルトプロパティを内部で持っていると思われる)。

実行結果

こんな風にスライダーを動かすと、ラベルの数値が変化します。

20141126_02

サンプルコード

サンプルはこちら
http://1drv.ms/1CeHWYp

カテゴリー: Xamarin, iOS パーマリンク