且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

MvvmCross Monotouch C#-绑定Int属性-模式:TwoWay

更新时间:2023-10-06 23:24:34

为了使绑定在View和ViewModel之间传输任何值,则当值更改时,它需要挂接到某个事件中.

In order for a binding to transfer any value between a View to a ViewModel, then it needs to hook into some event when the value changes.

在ViewModel中,此事件始终是INotifyProperty接口中的事件.

In the ViewModel, this event is always the event in the INotifyProperty interface.

在视图/活动"中,使用了一个单一模式-因此每个绑定都必须挂接到一个单独的事件中.例如,使用TextChanged事件连接EditText上的Text(请参见 MvxSeekBarProgressTargetBinging.cs ).

In the View/Activity, there is one single pattern employed - so each binding has to hook into a separate event. For example, the Text on EditText is hooked up using the TextChanged event (see MvxEditTextTextTargetBinding.cs) while the value in a SeekBar is hooked up using a Listener object rather than an event (see MvxSeekBarProgressTargetBinging.cs).

因此,如果您想为您的活动实现这种双向绑定,则可以通过以下方式实现:

So if you wanted to implement this two-way binding for your activity, then you could do this by:

  • 在您的活动(MyActivity)中声明一个事件-CurrentIndexChanged-每当CurrentIndex更改时都会触发
  • 为MyActivity声明自定义绑定,该绑定以编程方式链接CurrentIndex和CurrentIndexChanged
  • 在安装过程中将自定义绑定添加到绑定注册表中

例如,您的活动可能包括:

For example, your activity might include:

public event EventHandler CurrentIndexChanged;

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

然后您可以声明一个绑定类,如:

And you might then declare a binding class like:

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;
        }
    }
}

您需要通过以下方式告知绑定系统有关此绑定的信息:

And you'd need to tell the binding system about this binding in setup like:

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


但是...在实践上,如果您使用C#而不是XML进行操作,那么在这种情况下,***使用C#来简单地更新ViewModel,而不是在这种情况下使用声明性绑定.


However... at a practical level, if you are operating in C# rather than in XML, then you might be better off in this case using C# to simply update the ViewModel rather than using declarative binding in this case.

要清楚...在这种情况下,我很可能只是将Activity属性写为:

To be clear... in this case, I would most probably just write the Activity property as:

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

或者...我会考虑完全不在Activity中使用此属性.

Or... I'd consider not having this property in the Activity at all.

如果有帮助,请参见以下有关自定义绑定的更多信息:

If it helps, there's some more information on custom bindings in:

  • MonoTouch MVVMCross binding to instance variables
  • In MvvmCross how do I do custom bind properties

希望这会有所帮助!恕我直言,在使用XML时,绑定可以为您提供帮助-您不必使用它们...

Hope this helps! IMHO the bindings are there to help you when you're working in XML - you don't have to use them...

斯图尔特

更新如果要执行许多操作并遵循相同的名称模式,请使用名为 X 的属性以及已更改的名为 XChanged 的EventHandler事件>然后类似的事情可能会起作用-它使用反射来自动找到事件:

UPDATE If you are going to do lots of these and follow the same name pattern - using property named X with changed EventHandler event named XChanged then something like this might work - it uses reflection to find the event automagically:

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) });
        }
    }
}