davidwinchurch
davidwinchurch

Reputation: 85

MvvmCross and UIButton.Selected UISegmentedControl Bindings, iOS

In a cross platform Xamarin app built with the MvvmCross framework I'm using a ToggleButton Widget in an Android .axml layout. I've bound the Checked property to a View Model property using a converter using the following binding syntax:

Checked MarketBuySellViewModel.Direction, Converter=DirectionBool, ConverterParameter='Sell'

Everything works well. On the iOS side, it appears you can use UIButton as a ToggleButton by using the Selected property. This implies that the following binding should achieve what I want on iOS:

set.Bind (SellButton).For(b => b.Selected).To (vm => vm.MarketBuySellViewModel.Direction).WithConversion("DirectionBool", "Sell");

I don't get any binding errors in the application output but the binding itself doesn't seem to work. Clicking the button doesn't set the Direction property and setting the direction to a different value does not set the Selected property on the UIButton.

Do I need to create a Custom Binding or am I simply setting up the binding incorrectly?

I also tried using a UISegmentedControl to achieve the same effect. Is binding to this control supported at all in MvvmCross? I don't see any reference to it in the source code. Does this mean I need to create custom bindings for it too?

Upvotes: 1

Views: 3078

Answers (2)

kalitsov
kalitsov

Reputation: 1649

Could be helpful to someone else, so i'm sharing my experience. I needed a two way binding for UISegmentedControl.SelectedSegment property to a ViewModel. The one way biding (ViewModel => View) works by default. I couldn't able to properly utilize the solution proposed by Stuart - to subclass the UISegmentedControl. I tried to ensure that the linker does not rip off the new custom control code, but this didn't help me a bit. So a perfectly viable solution is the one with MvxPropertyInfoTargetBinding. Here is the code working ok for me:

public class MvxUISegmentedControlSelectedSegmentTargetBinding : MvxPropertyInfoTargetBinding<UISegmentedControl>
{
    public MvxUISegmentedControlSelectedSegmentTargetBinding(object target, PropertyInfo targetPropertyInfo)
        : base(target, targetPropertyInfo)
    {
        this.View.ValueChanged += HandleValueChanged;
    }

    private void HandleValueChanged(object sender, System.EventArgs e)
    {
        var view = this.View;
        if (view == null)
        {
            return;
        }
        FireValueChanged(view.SelectedSegment);
    }

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

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

public class Setup : MvxTouchSetup
{
    ...
    protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
    {
        registry.RegisterPropertyInfoBindingFactory(typeof(MvxUISegmentedControlSelectedSegmentTargetBinding), typeof(UISegmentedControl), "SelectedSegment");
    }
}

Upvotes: 1

Stuart
Stuart

Reputation: 66882

For the UIButton, I don't believe there's any included Selected binding built into MvvmCross. Because of this - and because Selected doesn't have a simple paired event SelectedChanged, then I believe Selected binding should work one-way (from ViewModel to View) but not two-way.

There is a binding for the On of a UISwitch control and that's the control I've seen used most in these situations.


If you wanted to add a custom 2-way binding for Selected then I guess you'd have to do this using the ValueChanged event (but would need to check that is correct).

To do so, you'd just build a target binding something like:

public class MvxUIButtonSelectedTargetBinding : MvxPropertyInfoTargetBinding<UIButton>
{
    public MvxUIButtonSelectedTargetBinding(object target, PropertyInfo targetPropertyInfo)
        : base(target, targetPropertyInfo)
    {
        var view = View;
        view.ValueChanged += HandleValueChanged;
    }

    private void HandleValueChanged(object sender, System.EventArgs e)
    {
        var view = View;
        if (view == null)
            return;
        FireValueChanged(view.Selected);
    }

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

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

and this could be registered in Setup in protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry) using something like:

registry.RegisterPropertyInfoBindingFactory(typeof(MvxUIButtonSelectedTargetBinding), typeof(UIButton),
                                           "Selected");

Similarly, I don't believe anyone has added a two way UISegmentedControl binding yet - but would happily see one added.

Building a two way UISegmentedControl binding would be quite straight-forward - you'd just have to bind to the pair SelectedSegment and ValueChanged - with code similar to above.

Alternatively, you could switch to using a custom MySegmentedControl which had a nicer Value`ValueChanged` pair which would automatically work without a custom binding - e.g.:

    public class MySegmentedControl : UISegmentedControl
    {
        // add more constructors if required

        public int Value
        {
            get { return base.SelectedSegment; }
            set { base.SelectedSegment = value; }
        }
    }

If any or all of these custom bindings are needed, then the Mvx project is happy to get these bindings added as issues or pull requests along with test/demo UIs in the https://github.com/slodge/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Touch/Views/FirstView.cs project

Upvotes: 1

Related Questions