Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MvvmCross: UIDatePicker time change does not trigger binding

I'm using Xamarin.iOS and MvvmCross to build a page view. I have a UIView containing a UIDatePicker in DateAndTime Mode. If I put a breakpoint in the bound vm property and change the date I can see the binding fire. If I change the time however nothing is updated on the bound property.

Here is the code:

[Register("EditJobViewJobView")]
public class EditJobView : MvxViewController
{
    public new EditJobViewModel ViewModel
    {
        get { return (EditJobViewModel)base.ViewModel; }
    }

    private const float _leftMargin = 6;
    private const float _labelHeight = 20;
    private const float _pickerHeight = 28;

    private readonly UIFont _labelFont = UIFont.BoldSystemFontOfSize(18f);
    private readonly UIFont _controlFont = UIFont.SystemFontOfSize(18f);

    private readonly UIView _paddingInsert = new UIView(new RectangleF(0, 0, 4, 0));

    private int _currentTop = 0;

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

        View = new UIView() { BackgroundColor = UIColor.White };

        NavigationItem.SetRightBarButtonItem(new UIBarButtonItem("Save", UIBarButtonItemStyle.Bordered,
            (sender, args) => ViewModel.OkCommand.Execute(null)), true);

        // ios7 layout
        if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
            EdgesForExtendedLayout = UIRectEdge.None;

        Title = "Edit Job";

        AddLabel("Start On");
        UIDatePicker startOnPicker;
        var startOnView = AddDateTimeView(out startOnPicker);

        var set = this.CreateBindingSet<EditJobView, EditJobViewModel>();
        set.Bind(startOnPicker).For("DateAndTime").To(vm => vm.TestStartOn);
        set.Bind(startOnView).To(vm => vm.TestFormatted);
        set.Apply();
    }

    private void AddLabel(string caption)
    {
        _currentTop += 10;
        var frame = new RectangleF(_leftMargin, _currentTop, 300, _labelHeight);
        var label = new UILabel(frame);
        label.Font = _labelFont;
        label.Text = caption;
        AddView(label);
        _currentTop += 2;
    }

    private UITextField AddDateTimeView(out UIDatePicker datePicker)
    {
        var textField = AddTextField();

        datePicker = new UIDatePicker();
        datePicker.Mode = UIDatePickerMode.DateAndTime;

        textField.InputView = datePicker;

        return textField;
    }

    private UITextField AddTextField()
    {
        var frame = new RectangleF(_leftMargin, _currentTop, 300, _pickerHeight);
        var textField = new UITextField(frame);
        textField.Layer.BorderColor = UIColor.Black.CGColor;
        textField.Layer.BorderWidth = 1f;
        textField.Font = _controlFont;
        textField.LeftView = _paddingInsert;
        textField.LeftViewMode = UITextFieldViewMode.Always;
        AddView(textField);
        return textField;
    }

    private void AddView(UIView view)
    {
        View.AddSubview(view);
        _currentTop += (int)view.Frame.Height;
    }
}

Any ideas?

UPDATE

This is the working custom binding code:

public class DatePickerDateAndTimeTargetBinding : MvxConvertingTargetBinding
{
    public DatePickerDateAndTimeTargetBinding(object target) : base(target)
    {
    }

    public override Type TargetType
    {
        get { return typeof(DateTime); }
    }

    protected UIDatePicker DatePicker
    {
        get { return (UIDatePicker) Target; }
    }

    public override void SubscribeToEvents()
    {
        DatePicker.ValueChanged += DatePickerValueChanged;
    }

    private void DatePickerValueChanged(object sender, EventArgs e)
    {
        var target = Target as UIDatePicker;

        if (target == null)
            return;

        var value = GetDate(target);
        FireValueChanged(value);
    }

    protected override void SetValueImpl(object target, object value)
    {
        var datePicker = (UIDatePicker) target;
        datePicker.Date = (DateTime)value;
    }

    private DateTime GetDate(UIDatePicker datePicker)
    {
        var dateTime = DateTime.SpecifyKind(datePicker.Date, DateTimeKind.Unspecified);
        var localDateTime = dateTime.ToLocalTime();
        return localDateTime;
    }

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

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            var target = Target as UIDatePicker;
            if (target != null)
            {
                target.ValueChanged -= DatePickerValueChanged;
            }
        }

        base.Dispose(isDisposing);
    }

}

And here is the startup code to wire it up:

protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
    registry.RegisterCustomBindingFactory<UIDatePicker>(
                    "DateAndTime",
                    binary => new DatePickerDateAndTimeTargetBinding(binary));
    base.FillTargetFactories(registry);
}
like image 912
Steve Chadbourne Avatar asked Dec 14 '25 13:12

Steve Chadbourne


1 Answers

The Date binding only binds the Date - see the source in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Touch/Target/MvxUIDatePickerDateTargetBinding.cs

If you wanted to create your own custom DateAndTime binding, then that should be very easy to do using the source from https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Touch/Target/MvxUIDatePickerDateTargetBinding.cs and https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Touch/Target/MvxUIDatePickerTimeTargetBinding.cs - you can then register your custom binding during setup (see N+1 videos N=28 for how to register custom bindings)

like image 136
Stuart Avatar answered Dec 19 '25 06:12

Stuart



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!