在ListView上单击ImageButton时,MVVMCross是否有办法更改选择器?

时间:2012-10-05 02:01:03

标签: android listview xamarin.android selector mvvmcross

问题与不使用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方式”而不是真正的跨平台(我让你想象可怕的代码来初始化所有这些东西)

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

雨果

1 个答案:

答案 0 :(得分:1)

部分地说,感觉这只是'UI eye candy' - 因此属于'View Concer'的范畴 - 因此mvvmcross通常不会尝试跨平台。

然而......我认为有办法。

如果ViewModel中的命令处理程序也在ViewModel上设置了一个CurrentSelectedPosition整数,那么每个UI都可以将SelectedItemPosition从ViewModel绑定到每个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(也可能是列表项)并使用这些类来更新选择不太痛苦的方式。