2

I can't understand, how can I bind two custom property between View and ViewMode with Two-Way mode. First I have a same ViewModel like:

//ViewModel
    public class MyViewModel : MvxViewModel
    {
    ....

    private MyMode _testA

    public MyMode TestA
    {
        get => _testA;
        set { 
            _testA = value;
            RaisePropertyChanged(()=> TestA);
            }
    }


        public  MyViewModel()
        {
            TestA = MyMode.A;
        }

            ........
    }

And in View I do bind with my custom property:

//View
 public partial class MyView : MvxViewController<MyViewModel>
 {

    public MyMode UiTestA

     private void SetBiding()
        {
            var set = this.CreateBindingSet<MyView, MyViewModel>();
            set.Bind(this).For(x => x.UiTestA()).To(vm => vm.TestA);
            set.Apply();
        }

    private void SomeMethod()
        {
            var t1 = UiTestA; // t1 = MyMode.A;
            UiTestA = MyMode.B; // Two way binding?
            var t2 = ViewModel.TestA; // MyMode.A; 
        }
 }

If I change TestA in ViewModel, I can get this in View, but I want change it in View and find new value in ViewModel.

  • To clarify what Cheesebaron answered, the reason the VM property wasn't updated was because nothing triggered a change notification from the View side. In your VM, you use RaisePropertyChanged. On the View side, you will create the TargetBinding as below. The reason Mvx works out of the box is that things like Text and Click have bindings already defined in the Mvx framework. – Kiliman Aug 17 '17 at 21:32

1 Answers1

1

Per default MvvmCross can bind any public properties, in OneWay mode. To get TwoWay mode working you need to create at target binding which allows to set from Target to Source. These are called TargetBindings.

Lets say your view has a property called Hello and an event called HelloChanged. With these two in hand you can create a simple TargetBinding:

public class MyViewHelloTargetBinding
    : MvxConvertingTargetBinding
{
    protected MyView View => Target as MyView;

    private bool _subscribed;

    public MyViewHelloTargetBinding(MyView target)
        : base(target)
    {
    }

    private void HandleHelloChanged(object sender, EventArgs e)
    {
        var view = View;
        if (view == null) return;

        FireValueChanged(view.Hello);
    }

    public override MvxBindingMode DefaultMode = MvxBindingMode.TwoWay;

    public override void SubscribeToEvents()
    {
        var target = View;
        if (target == null)
        {
            MvxBindingTrace.Trace(MvxTraceLevel.Error,
                "Error - MyView is null in MyViewHelloTargetBinding");
            return;
        }

        target.HelloChanged += HandleHelloChanged;
        _subscribed = true;
    }

    public override Type TargetType => typeof(string);

    protected override void SetValueImpl(object target, object value)
    {
        var view = (MyView)target;
        if (view == null) return;

        view.Hellp = (string)value;
    }

    protected override void Dispose(bool isDisposing)
    {
        base.Dispose(isDisposing);
        if (isDisposing)
        {
            var target = View;
            if (target != null && _subscribed)
            {
                target.HelloChanged -= HandleHelloChanged;
                _subscribed = false;
            }
        }
    }
}

Then you just need to register your target binding in your Setup.cs file in FillTargetFactories:

registry.RegisterCustomBindingFactory<MyView>(
    "Hello", view => new MyViewHelloTargetBinding(view));
Cheesebaron
  • 24,131
  • 15
  • 66
  • 118