質問

I've been converting an existing application over to use MvvmCross (HotTuna) and one of the Android controls I've used a fair bit is the RatingBar.

I'm having trouble getting 2-way binding working on this control and I suspect that I should implement an MvxRatingBar in a similar style to the other Android controls under Cirrious.MvvmCross.Binding.Droid.Views.

Currently using just standard binding I have the following code:

    <RatingBar
    style="@style/scoreRatingBar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:isIndicator="false"
    android:paddingTop="5dp"
    local:MvxBind="Max OutOf; NumStars OutOf; Rating ScoreA, Mode=TwoWay" />

My question is, would this be the recommended way to solve the problem of two-way binding on a control such as RatingBar?

役に立ちましたか?

解決

MvvmCross two-way binding "just works" for any control which provides a property API which follows a "Changed" pattern like:

public PType MyProperty { get; set; }
public event EventHandler MyPropertyChanged;

For controls which don't follow this convention or which use a specialised EventHandler, you either have to:

1. Inherit from the control and provide the pattern in the inherited control class

public class ExtraThing : Thing
{
    public ExtraThing(Context c, IAttributeSet attrs)
         : base(c, attrs)
    {
    }

    protected override SomethingChanged()
    {
        MyPropertyChanged.Raise(this);
    }

    public PType MyProperty
    {
       get { return base.Something(); }
       set { base.ChangeSomething(value); }
    }

    public event EventHandler MyPropertyChanged;
}

2. Or provide a custom target binding

This is discussed in full in http://slodge.blogspot.co.uk/2013/06/n28-custom-bindings-n1-days-of-mvvmcross.html

The code basically required is for a custom 'target binding' which knows how to get/set the property and how to observe its changes:

public class ThingMyPropertyTargetBinding : MvxAndroidTargetBinding
{
    protected Thing Thing
    {
        get { return (Thing) Target; }
    }

    public ThingMyPropertyTargetBinding (Thing target) : base(target)
    {
    }

    public override void SubscribeToEvents()
    {
        Thing.MyPropertyChanged += TargetOnMyPropertyChanged;
    }

    private void TargetOnMyPropertyChanged(object sender, SpecialEventArgs eventArgs)
    {
        var target = Target as Thing;

        if (target == null)
            return;

        var value = target.GetMyProperty();
        FireValueChanged(value);
    }

    protected override void SetValueImpl(object target, object value)
    {
        var binaryEdit = (Thing)target;
        binaryEdit.SetMyProperty((PType)value);
    }

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

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

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            var target = Target as BinaryEdit;
            if (target != null)
            {
                target.MyPropertyChanged -= TargetOnMyPropertyChanged;
            }
        }
        base.Dispose(isDisposing);
    }
}

This "TargetBinding" can then be registered during setup time using code like:

    protected override void FillTargetFactories(Cirrious.MvvmCross.Binding.Bindings.Target.Construction.IMvxTargetBindingFactoryRegistry registry)
    {
        registry.RegisterCustomBindingFactory<Thing>(
                        "SpecialBinding",
                        thing => new ThingMyPropertyTargetBinding (thing) );
        base.FillTargetFactories(registry);
    }

For RatingBar, I think either of these approaches would work ... and I think it would also be useful to get back into the core project for others to use.

他のヒント

Just for reference, Rating target binding is built in newer versions of Mvx as you can see here: List of local:MvxBind binders

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top