3

MvvmCrossは初めてですが、質問があります。

次のバインディングコードは1つの方法でしか機能しないことに気づきました。

{ this, "{'CurrentIndex':{'Path':'CurrentIndex','Mode':'TwoWay'}}" }
  • CurrentIndexは、ビューのIntプロパティです
  • CurrentIndexは、ViewModelのIntプロパティでもあります

このように機能します!

  • ViewModel=>表示

しかし、この方法ではありません!

  • ビュー=>ViewModel

私はViewControllerのコレクションを持っており、私の目標は、viewModelのCurrentIndexに対してDeleteCommandを呼び出すことでした。

でも、

「AndroidとTouchの2ウェイバインディングは不完全です」

参照:MvvmCrossの経験、後知恵、制限?

私の推測では、TwoWayモードはコントロール(UILabel、UITextfield、...)でのみ機能し、プロパティでは機能しません。

それで、それを両方の方法で機能させる良い方法はありますか?または私の問題に代わるものはありますか?

パトリック

4

1 に答える 1

4

バインディングがビュー間で任意の値をViewModelに転送するには、値が変更されたときに何らかのイベントにフックする必要があります。

ViewModelでは、このイベントは常にINotifyPropertyインターフェイスのイベントです。

ビュー/アクティビティでは、1つの単一パターンが採用されているため、各バインディングは個別のイベントにフックする必要があります。たとえば、EditTextのテキストはTextChangedイベントを使用して接続され(MvxEditTextTextTargetBinding.csを参照)、SeekBarの値はイベントではなくListenerオブジェクトを使用して接続されます(MvxSeekBarProgressTargetBinging.csを参照)。

したがって、アクティビティにこの双方向バインディングを実装する場合は、次の方法でこれを行うことができます。

  • CurrentIndexが変更されるたびに発生するイベント(MyActivity)でのイベント(CurrentIndexChanged)の宣言
  • CurrentIndexとCurrentIndexChangedをプログラムでリンクするMyActivityのカスタムバインディングを宣言します
  • セットアップ中にバインディングレジストリにカスタムバインディングを追加する

たとえば、アクティビティには次のものが含まれます。

public event EventHandler CurrentIndexChanged;

private int _currentIndex;
public int CurrentIndex
{ 
   get { return _currentIndex; } 
   set { _currentIndex = value; if (CurrentIndexChanged != null) CurrentIndexChanged(this, EventArgs.Empty); } 
}

次に、次のようなバインディングクラスを宣言します。

public class MyBinding : MvxPropertyInfoTargetBinding<MyActivity>
{        
    public MyBinding (object target, PropertyInfo targetPropertyInfo) 
        : base(target, targetPropertyInfo)
    {
        View.CurrentIndexChanged += OnCurrentIndexChanged;
    }

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

    private void OnCurrentIndexChanged(object sender, EventArgs ignored)
    {
        FireValueChanged(View.CurrentIndex);
    }

    protected override void Dispose(bool isDisposing)
    {
        base.Dispose(isDisposing);
        if (isDisposing)
        {
            View.CurrentIndexChanged -= OnCurrentIndexChanged;
        }
    }
}

そして、次のようにセットアップでこのバインディングについてバインディングシステムに通知する必要があります。

       registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(typeof(MyBinding), typeof(MyActivity), "CurrentIndex"));

ただし、実際のレベルでは、XMLではなくC#で操作している場合は、この場合、宣言型バインディングを使用するよりも、C#を使用してViewModelを更新する方がよい場合があります。

明確にするために...この場合、私はおそらくActivityプロパティを次のように記述します。

public int CurrentIndex
{ 
   get { return _currentIndex; } 
   set { _currentIndex = value; ViewModel.CurrentIndex = value; } 
}

または...アクティビティにこのプロパティをまったく含まないことを検討します。


それが役立つ場合は、カスタムバインディングに関する詳細情報が次の場所にあります。


お役に立てれば!IMHOバインディングは、XMLで作業しているときに役立つものです。バインディングを使用する必要はありません...

スチュアート


更新これらの多くを実行し、同じ名前パターンに従う場合(Xという名前のプロパティとXChangedという名前の変更されたEventHandlerイベントを使用すると、次のようなものが機能する可能性があります)、リフレクションを使用してイベントを自動的に検索します。

public class MyBinding<T> : MvxPropertyInfoTargetBinding<T>
    where T : class
{
    private readonly PropertyInfo _propertyInfo;
    private readonly EventInfo _eventInfo;

    public MyBinding(object target, PropertyInfo targetPropertyInfo)
        : base(target, targetPropertyInfo)
    {
        _propertyInfo = targetPropertyInfo;
        var eventName = _propertyInfo.Name + "Changed";
        _eventInfo = View.GetType().GetEvent(eventName);
        if (_eventInfo == null)
        {
            throw new MvxException("Event missing " + eventName);
        }

        if (_eventInfo.EventHandlerType != typeof(EventHandler))
        {
            throw new MvxException("Event type mismatch for " + eventName);
        }

        var addMethod = _eventInfo.GetAddMethod();
        addMethod.Invoke(View, new object[] { new EventHandler(OnChanged) });
    }

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

    private void OnChanged(object sender, EventArgs ignored)
    {
        var value = _propertyInfo.GetValue(View, null);
        FireValueChanged(value);
    }

    protected override void Dispose(bool isDisposing)
    {
        base.Dispose(isDisposing);
        if (isDisposing)
        {
            var removeMethod = _eventInfo.GetRemoveMethod();
            removeMethod.Invoke(View, new object[] { new EventHandler(OnChanged) });
        }
    }
}
于 2012-06-27T18:00:37.367 に答える