2

问题和不使用 MVVMCross 编程是一样的,但是不知道有没有“跨平台”的解决方案:

在 MvxBindableListView(或 ListView)上使用 ImageButton 时,我们必须放置一些选项才能在此按钮上使用操作:

        <ImageButton
            ...
            android:focusable="false"
            android:clickable="true" 
            ...
            local:MvxBind="'Click':{'Path':'Command1'}}"
          />

使用这些参数,按钮会对“Command1”做出反应。但问题是 ListView 的“视觉选择器”没有改变。

我们举个例子:

当 ListView 中有 5 行时,第一行被选中。我触摸第 3 行的 ImageButton,“Command1”会做出反应(与 ListView 的第 3 项),但选择器将停留在第一行。

所以,在 Android 中,我们必须这样写一些代码:

_imagebutton.Click += (object sender, EventArgs eventsArgs) =>
    {
        View v = ...
        MvxBindableListView l = ...
        int p = l.GetPositionForView(...);
        l.PerformItemClick(..., p, p); 
     };

使用这段代码,选择了正确的项目并且行为是正确的(只要我不想使用 ItemClick 触发真实事件)。但是这个解决方案是“Android方式”而不是真正的跨平台(我让你想象一下初始化所有这些东西的可怕代码)

有人有更好的解决方案吗?

雨果

4

1 回答 1

1

在某种程度上,这感觉就像这只是“UI 眼睛糖果”——因此属于“视图关注”的范围——因此 mvvmcross 通常不会尝试跨平台。

但是...我认为有办法。

如果 ViewModel 中的命令处理程序也在 ViewModel 上设置了 CurrentSelectedPosition 整数,则每个 UI 都可以将 ViewModel 中的 SelectedItemPosition 绑定到每个 ui 中的每个列表 - 这应该会导致 UI 本地更新选择。

我认为这会起作用......但在 Android 上它需要一些绑定:

public class MvxAdapterViewSelectedItemPositionTargetB-inging : MvxBaseAndroidTargetBinding
{
    private readonly AdapterView _adapterView;

    public MvxAdapterViewSelectedItemPositionTargetBinging(AdapterView adapterView)
    {
        _adapterView = adapterView;
        _adapterView.ItemSelected += AdapterViewOnItemSelected;
    }

    public override void SetValue(object value)
    {
        _adapterView.SetSelection((int)value);
    }

    private void AdapterViewOnItemSelected(object sender, AdapterView.ItemSelectedEventArgs itemSelectedEventArgs)
    {
        FireValueChanged(itemSelectedEventArgs.Position);
    }

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

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

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            if (_adapterView != null)
            {
                _adapterView.ItemSelected -= AdapterViewOnItemSelected;
            }
        }
        base.Dispose(isDisposing);
    }
}

注册使用:

        registry.RegisterFactory(new MvxCustomBindingFactory<AdapterView>("SelectedItemPosition", adapterView => new MvxAdapterViewSelectedItemPositionTargetBinging(adapterView)));

并绑定到 axml 中的 UI,例如:

<Mvx.MvxBindableListView
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    local:MvxBind="{'ItemsSource':{'Path':'Emails'},'SelectedItemPosition':{'Path':'CurrentSelectedPosition'}}"
    local:MvxItemTemplate="@layout/listitem_email"
/>

我使用 ViewModel 列表项所在的电子邮件列表测试了这个想法:

    public class SimpleEmail
    {
        public EmailViewModel Parent { get; set; }
        public string From { get; set; }    
        public string Header { get; set; }    
        public string Message { get; set; }    
        public ICommand Command1
        {
            get
            {
                return new MvxRelayCommand(() => Parent.CurrentSelectedPosition = Parent.Emails.IndexOf(this));
            }
        }
    }
  • 但显然这只是一个演示......

注意:我在上面的代码中使用了选定的位置而不是选定的对象 - 因为我知道您使用的列表很长!


如果您想考虑对仅适用于 android 的代码采用不同的方法,那么我认为您可以通过从 Mvx.MvxBindableListView (也可能是列表项)继承并使用这些类以可能不那么痛苦的方式更新选择来做到这一点.

于 2012-10-05T07:51:56.263 回答