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 で変更通知をします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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() の中身は空でも大丈夫です。

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

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

TipCalc.UI.Touch の中身

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

1
2
3
4
5
6
7
8
9
10
11
12
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 自体を変えてしまってもいいのですが。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[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;
することを忘れずに…いつもあれ?ってなるので。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 パーマリンク