1

I am trying to implement a custom binding on a subclass of UITextField so that the bound value is set when the user is done editing instead of with each keystroke because some interim values are invalid in the viewmodel (for example, while setting 'Age' to '26', a value of '2' is invalid so I'd like to wait to set the value until both digits are there). Something similar to setting UpdateSourceTrigger in xaml. I looked at several examples here: MvvmCross UITextField custom binding is similar, as is MvvmCross: change update source trigger property of binding on MonoDroid (but for Android). I've also watch N=28 custom binding and looked at the source for MvxUITextFieldTextTargetBinding.

I think I'm close, but my custom binding never gets created and the UITextFields in my app still FireValueChanged with every keystroke.

I created the following Custom Binding:

public class UITextFieldFocusChangedBinding : MvxTargetBinding
{
    private bool _subscribed;
    private UITextField _view;
    public UITextFieldFocusChangedBinding(UITextField target) : base(target)
    {
        _view = target;
    }

    public override void SetValue(object value)
    {

        if (_view == null) return; 
        _view.Text = (string)value;
    }

    public override void SubscribeToEvents()
    {
        var view = _view;
        if (view == null)
            return;
        view.Ended += TextFieldOnEnded;
    }

    private void TextFieldOnEnded(object sender, EventArgs eventArgs)
    {
        var view = _view;
        if (view == null)
            return;
        if (!view.IsFirstResponder)
            FireValueChanged(view.Text);
        _subscribed = true;

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

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

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            var view = _view;
            if (view != null && _subscribed)
            {
                view.Ended -= TextFieldOnEnded;
                _subscribed = false;
            }
        }
        base.Dispose(isDisposing);
    }

}

My setup.cs contains the following:

protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
    base.FillTargetFactories(registry);
    registry.RegisterPropertyInfoBindingFactory(typeof(Bindings.UITextFieldFocusChangedBinding),typeof(UITextField), "Text");
}

and in my MvxViewController I have:

var set = this.CreateBindingSet<LifeNeedsView, LifeNeedsViewModel>();
set.Bind(_txtFinMedExpenses).To(vm => vm.FinalMedicalExpenses);
set.Apply();

The bindings work (values are passing correctly) but with every keystroke. Any suggestions on what I might be missing?

Community
  • 1
  • 1
jyarnott
  • 23
  • 4
  • If you debug your app, does your binding get created at all? Is there any extra trace? I'm not sure suspicious that your code is using `registry.RegisterPropertyInfoBindingFactory` but is actually not using a `PropertyInfo` based binding class - so I think it should really be using `RegisterCustomBindingFactory` instead. – Stuart Apr 02 '14 at 21:38
  • You should also never hold on to `private UITextField _view` yourself - use the `WeakReference` supplied by the base class as `Target` - this can help to avoid leaks. Will try to write proper answer on this one day soon... – Stuart Apr 03 '14 at 12:06
  • @Stuart That took care of it, thanks. I did have to do one additional thing that I wondered about but forgot to ask in the original post so I'll mention it here. After the changes you specified, it still wasn't working. My custom binding wasn't being created. I couldn't register the custom binding for UITextField, I had to do it for each of my 3 subclasses, so: `registry.RegisterCustomBindingFactory("Text", tf => new UITextFieldFocusChangedBinding(tf));` instead of UITextField. – jyarnott Apr 03 '14 at 16:25
  • In that case, try registering your target binding as the very last thing in InitializeLastChance - similar to registrations in http://stackoverflow.com/questions/22622836/mvvmcross-bind-to-visibility-with-autolayout-and-fully-hide-view/22627799#22627799 - that way your reg will definitely be last. – Stuart Apr 03 '14 at 18:38
  • @Stuart. Thanks. I did try that before when I thought it might be my timing but in my Setup, when I follow the example in the link above, it can't resolve `base.Resolve` in the override of `InitializeLastChance`. Am I missing something? – jyarnott Apr 03 '14 at 18:53
  • Should be Mvx.Resolve - me and my mobile typing #sorry – Stuart Apr 03 '14 at 19:00
  • @Stuart. Thanks for all of your help, I'm all set now. – jyarnott Apr 07 '14 at 13:49

0 Answers0