android - MVVM 在 MvxBindableListView 中交叉更改 ViewModel

标签 android listview xamarin.android viewmodel mvvmcross

我的 Android 应用程序有点问题,我不知道如何使用 MVVM Cross 解决它。

这是我的模型

public class Article 
{
    string Label{ get; set; }
    string Remark { get; set; }
}

我的 View 模型

public class ArticleViewModel: MvxViewModel
{
    public List<Article> Articles;
    ....

}

我的layout.axml ...

    <LinearLayout
        android:layout_width="0dip"
        android:layout_weight="6"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        android:id="@+id/layoutArticleList">
        <EditText
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/editSearch"
            android:text=""
            android:singleLine="True"
            android:selectAllOnFocus="true"
            android:capitalize="characters"
            android:drawableLeft="@drawable/ic_search_24"
            local:MvxBind="{'Text':{'Path':'Filter','Mode':'TwoWay'}}"
            />
      <Mvx.MvxBindableListView
            android:id="@+id/listviewArticle"
            android:choiceMode="singleChoice"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" 
            local:MvxItemTemplate="@layout/article_rowlayout"
            local:MvxBind="{'ItemsSource':{'Path':'Articles'}}" />                
    </LinearLayout>
...

我的问题来了,“article_rowlayout”

...
<TableRow
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/blue">
        <TextView
            android:id="@+id/rowArticleLabel"
            android:layout_width="0dip"
            android:layout_weight="14"
            android:layout_height="wrap_content"
            android:textSize="28dip"
            local:MvxBind="{'Text':{'Path':'Label'}}" />
        <ImageButton
            android:src="@drawable/ic_modify"
            android:layout_width="0dip"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:id="@+id/rowArticleButtonModify"
            android:background="@null" 
            android:focusable="false"
            android:clickable="true"    
            local:MvxBind="{'Click':{'Path':'MyTest'}}"
          />
...

名为“MyTest”的“单击”命令链接到 MvxBindableListView 提供的项目上。换句话说,单击在我的模型“文章”中搜索命令“MyTest”,而不是我的 ViewModel。如何更改该行为以链接负责我的 MvxBindableListView 的 ViewModel“ArticleViewModel”?

有什么建议吗?

最佳答案

关于点击事件尝试绑定(bind)的位置,您的分析绝对正确。

我通常采用两种方法:

  1. 在列表中使用 ItemClick
  2. 继续使用 Click,但在 ViewModel 端做一些重定向。

所以...1

Main Menu在教程中有一个 ViewModel 有点像:

public class MainMenuViewModel
    : MvxViewModel
{
    public List<T> Items { get; set; }

    public IMvxCommand ShowItemCommand
    {
        get
        {
            return new MvxRelayCommand<T>((item) => /* do action with item */ );
        }
    }
}

这在 axml 中用作:

<Mvx.MvxBindableListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res/Tutorial.UI.Droid"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    local:MvxBind="{'ItemsSource':{'Path':'Items'},'ItemClick':{'Path':'ShowItemCommand'}}"
    local:MvxItemTemplate="@layout/listitem_viewmodel"
  />

此方法只能用于整个列表项上的 ItemClick - 不能用于列表项中的单个 subview 。


或者...2

因为我们没有任何 RelativeSource mvx 中的绑定(bind)指令,这种类型的重定向可以在 ViewModel/Model 代码中完成。

这可以通过呈现模型对象的启用行为的包装器而不是模型对象本身来完成 - 例如使用 List<ActiveArticle> :

public ActiveArticle
{
   Article _article;
   ArticleViewModel _parent;

   public WrappedArticle(Article article, ArticleViewModel parent)
   {
       /* assignment */
   }

   public IMvxCommand TheCommand { get { return MvxRelayCommand(() -> _parent.DoStuff(_article)); } }

   public Article TheArticle { get { return _article; } } 
}

然后您的 axml 将不得不使用如下绑定(bind):

    <TextView            ...
        local:MvxBind="{'Text':{'Path':'TheArticle.Label'}}" />

    <ImageButton
        ...
        local:MvxBind="{'Click':{'Path':'TheCommand.MyTest'}}" />

此方法的一个示例是使用 WithCommand 的 session 示例

但是...请注意,使用WithCommand<T>时我们发现了内存泄漏 - 基本上 GarbageCollection 拒绝收集嵌入的 MvxRelayCommand - 这就是为什么 WithCommand<T>IDisposable为什么BaseSessionListViewModel清除列表并在分离 View 时处理 WithCommand 元素。


评论后更新:

如果您的数据列表很大 - 并且您的数据是固定的(您的文章是没有 PropertyChanged 的​​模型)并且您不想承担创建大型 List<WrappedArticle> 的开销那么解决这个问题的一种方法可能是使用 WrappingList<T>类。

这与 Microsoft 代码中采用的方法非常相似 - 例如在 WP7/Silverlight 中虚拟化列表 - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

对于您的文章,这可能是:

public class ArticleViewModel: MvxViewModel
{
    public WrappingList<Article> Articles;

    // normal members...
}

public class Article
{
    public string Label { get; set; }
    public string Remark { get; set; }
}

public class WrappingList<T> : IList<WrappingList<T>.Wrapped>
{
    public class Wrapped
    {
        public IMvxCommand Command1 { get; set; }
        public IMvxCommand Command2 { get; set; }
        public IMvxCommand Command3 { get; set; }
        public IMvxCommand Command4 { get; set; }
        public T TheItem { get; set; }
    }

    private readonly List<T> _realList;
    private readonly Action<T>[] _realAction1;
    private readonly Action<T>[] _realAction2;
    private readonly Action<T>[] _realAction3;
    private readonly Action<T>[] _realAction4;

    public WrappingList(List<T> realList, Action<T> realAction)
    {
        _realList = realList;
        _realAction = realAction;
    }

    private Wrapped Wrap(T item)
    {
        return new Wrapped()
            {
                Command1 = new MvxRelayCommand(() => _realAction1(item)),
                Command2 = new MvxRelayCommand(() => _realAction2(item)),
                Command3 = new MvxRelayCommand(() => _realAction3(item)),
                Command4 = new MvxRelayCommand(() => _realAction4(item)),
                TheItem = item
            };
    }

    #region Implementation of Key required methods

    public int Count { get { return _realList.Count; } }

    public Wrapped this[int index]
    {
        get { return Wrap(_realList[index]); }
        set { throw new NotImplementedException(); }
    }

    #endregion

    #region NonImplementation of other methods

    public IEnumerator<Wrapped> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Add(Wrapped item)
    {
        throw new NotImplementedException();
    }

    public void Clear()
    {
        throw new NotImplementedException();
    }

    public bool Contains(Wrapped item)
    {
        throw new NotImplementedException();
    }

    public void CopyTo(Wrapped[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public bool Remove(Wrapped item)
    {
        throw new NotImplementedException();
    }

    public bool IsReadOnly { get; private set; }

    #endregion

    #region Implementation of IList<DateFilter>

    public int IndexOf(Wrapped item)
    {
        throw new NotImplementedException();
    }

    public void Insert(int index, Wrapped item)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    #endregion
}   

关于android - MVVM 在 MvxBindableListView 中交叉更改 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12682082/

相关文章:

java - 如何将 ArrayList 中的类的类从一个 Activity 传递到另一个 Activity

android - transformNativeLibsWithStripDebugSymbolForRelease 执行失败,mips64el-linux-android-strip

android - 无法获取 facebook sdk 示例中好友列表的所有字段以及在 Android 中仅获取 id、name 的好友选择器示例中的好友列表

java - 在单独的类中加载 JSON 数组列表并在另一个 Activity 中加载

listview - [UWP][XAML] ListView 子项未使用全宽

xamarin - 在 Xamarin 中找不到 Android SDK

android - 如何以编程方式创建 TextView、ImageView 和相关布局

java - 将文本文件读入 ListView(应用程序添加到 ListView 两次)?

xamarin - 如何在 Xamarin Forms 应用程序中的 TabbedPage 栏项目上设置 AutomationId 以进行 UI 测试

xamarin - CallygraphyXamarin 无法在 MvxAppCompatActivity 中工作