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

时间:2012-06-27 17:12:00

标签: mvvm xamarin.ios mvvmcross

我是MvvmCross的新手,我有一个问题。

我注意到以下绑定代码仅以一种方式工作:

{ this, "{'CurrentIndex':{'Path':'CurrentIndex','Mode':'TwoWay'}}" }
  • CurrentIndex是视图中的Int属性
  • CurrentIndex也是ViewModel中的Int属性

这种方式有效!

  • ViewModel =>图

但不是这样!

  • 查看=>视图模型

我有一组ViewControllers,我的目标是为viewModel中的CurrentIndex调用DeleteCommand。

然而,

  

“Android和Touch 2路绑定不完整”

参考:MvvmCross experiences, hindsight, limitations?

我的猜测是TwoWay模式仅适用于Controls(UILabel,UITextfield,...),但不适用于属性。

那么,有没有一种方法可以让它以两种方式运作?或者我的问题还有其他选择吗?

帕特里克

1 个答案:

答案 0 :(得分:4)

为了使绑定在View之间传递任何值到ViewModel,它需要在值改变时挂钩到某个事件。

在ViewModel中,此事件始终是INotifyProperty界面中的事件。

在View / Activity中,使用了一个单一模式 - 因此每个绑定都必须挂钩到一个单独的事件中。例如,EditText上的Text使用TextChanged事件(请参阅MvxEditTextTextTargetBinding.cs)连接,而SeekBar中的值使用Listener对象而不是事件(请参阅MvxSeekBarProgressTargetBinging.cs)连接。

因此,如果您想为您的活动实现此双向绑定,那么您可以通过以下方式执行此操作:

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

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

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

但是......在实际层面上,如果你使用的是C#而不是XML,那么在这种情况下你可能会更好,使用C#来简单地更新ViewModel而不是在这种情况下使用声明性绑定。

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

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

或者......我认为在Activity中根本没有这个属性。


如果有帮助,可以在以下网站上找到有关自定义绑定的更多信息:


希望这有帮助!恕我直言,当您使用XML工作时,绑定可以帮助您 - 您不必使用它们......

斯图尔特


更新如果您打算执行大量这些操作并遵循相同的名称模式 - 使用名为 X 的属性更改名为 XChanged 那么像这样的东西可能会起作用 - 它使用反射来自动找到事件:

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