eMi
eMi

Reputation: 5628

Bind TimePicker & DatePicker - MVVMCross (Mono For Android)

How to bind a Time - & /DatePicker with MVVMCross in Mono For Android?

  <TimePicker
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

  <DatePicker
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

And what are the types of the Properties behind? For Date, probably DateTime and for the TimePicker? Or do we have here a Property foreach Number, for Example for Date: Year (INT), Month (INT) and Day (INT) = 3 Properties?

Stuck at this one and didn't find any examples..

Help appreciated

Upvotes: 2

Views: 4247

Answers (1)

Stuart
Stuart

Reputation: 66882

The DatePicker API from Android is pretty disgusting...

However, binding it should be pretty easy :)

What I would do is to add a custom control and a custom binding.


For some help on Custom Droid controls, see

For some other questions about Custom Bindings, see

and other links from http://slodge.blogspot.co.uk/p/mvvmcross-quicklist.html


The Android DatePicker API was clearly written by Kermit during some downtime, so for the DatePicker, I would probably suggest is that you implement an EmiDatePicker that inherits from DatePicker and tidies up the API.

This can provide a single property and a single event:

   private bool _initialised;

   public DateTime Value
   {
      get 
      {
         return new DateTime(Year, Month.ToCLRMonthNumber(), DayOfMonth);
      }
      set
      {
         if (_initialised)
         {
             UpdateDate(value.Year, value.Month.ToJavaMonthNumber(), value.DayOfMonth);
         }
         else
         {
             _initialised = true;
             Init(value.Year, value.Month.ToJavaMonthNumber(), value.DayOfMonth, new EmiListener(this));
         }
      }
   }

   public event EventHandler ValueChanged;

where:

  • ToJavaMonthNumber, ToCLRMonthNumber are workarounds for +1, -1 fixes - see MonthDisplayHelper.NumberOfDaysInMonth returning incorrect value in MonoDroid
  • EmiListener is a simple implementation of IOnDateChangeListener to fire ValueChanged - something like:

    public class EmiListener : Java.Lang.Object, DatePicker.IOnDateChangedListener
    {
        private EmiDatePicker _datePicker;
    
        public EmiListener(EmiDatePicker datePicker)
        {
            _datePicker = datePicker;
        }
    
        public void OnDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)
        {
            _datePicker.InternalSetValueAndRaiseChanged(new DateTime(year, monthOfYear.ToCLRMonth(), dayOfMonth));
        }
    }
    

With this done... then the next code step would be to add a binding - this would be simple:

public class EmiDatePickerValueTargetBinding : MvxPropertyInfoTargetBinding<EmiDatePicker>
{
    public EmiDatePickerValueTargetBinding(object target, PropertyInfo targetPropertyInfo)
        : base(target, targetPropertyInfo)
    {
        var datePicker = View;
        if (datePicker == null)
        {
            MvxBindingTrace.Trace(MvxTraceLevel.Error, "Error - datePicker is null in EmiDatePickerValueTargetBinding");
        }
        else
        {
            datePicker.ValueChanged += DatePickerOnValueChanged;
        }
    }

    public override MvxBindingMode DefaultMode
    {
        get { return MvxBindingMode.TwoWay; }
    }

    private void DatePickerOnValueChanged(object sender, EventArgs args)
    {
        FireValueChanged(View.Value);
    }

    protected override void Dispose(bool isDisposing)
    {
        base.Dispose(isDisposing);
        if (isDisposing)
        {
            var datePicker = View;
            if (datePicker != null)
            {
                datePicker.ValueChanged -= DatePickerOnValueChanged;
            }
        }
    }
}

And to register this binding, just add it in your app's setup class:

    protected override void FillTargetFactories(Cirrious.MvvmCross.Binding.Interfaces.Bindings.Target.Construction.IMvxTargetBindingFactoryRegistry registry)
    {
        registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(
                                     typeof (EmiDatePickerValueTargetBinding), typeof (EmiDatePicker), "Value"));

        base.FillTargetFactories(registry);
    }

At this point, you can finally just use an EmiDatePicker in your XML - although to save on typing I'd recommend using an XML abbreviation - http://slodge.blogspot.co.uk/2013/02/abbreviated-namespaces-for-your-custom.html


To do TimePicker... same again, but I'll leave that to you...


There will be typos in the above code - please do edit it to correct it.

If this code works... and since it is a lot of 'mechanical code', I would love to see it open-sourced (maybe even pushed back to MvvmCross)

Upvotes: 1

Related Questions