MVVMCross 在 MvxBindableListView 中更改 ViewModel [英] MVVMCross changing ViewModel within a MvxBindableListView

查看:23
本文介绍了MVVMCross 在 MvxBindableListView 中更改 ViewModel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Little problem with my Android application and I don't know how to solve it with MVVM Cross.

这是我的模型

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

我的视图模型

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

}

我的layout.axml...

My 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"

And here comes my problem, the "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"的Click"命令链接在 MvxBindableListView 给出的项目上.换句话说,单击在我的模型文章"中搜索命令MyTest",而不是我的 ViewModel.如何更改该行为以链接负责我的 MvxBindableListView 的 ViewModelArticleViewModel"?

The "Click" command called "MyTest" is linked on the item given by the MvxBindableListView. In other words, Click search for a command "MyTest" in my model "Article", instead of my ViewModel. How can I change that behaviour in order to link my ViewModel "ArticleViewModel" which is responsible of my MvxBindableListView?

有什么建议吗?

推荐答案

关于点击事件试图绑定的位置,您的分析绝对正确.

Your analysis is definitely correct about where the click event is trying to bind.

我通常采用两种方法:

  1. 使用项目点击列表
  2. 继续使用 Click,但在 ViewModel 端进行一些重定向.

<小时>

所以...1


So...1

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

The Main Menu in the tutorial has a ViewModel a bit like:

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

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

这在 axml 中用作:

This is used in axml as:

<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 执行 - 不能在列表项内的单个子视图上执行.

This approach can only be done for ItemClick on the whole list item - not on individual subviews within the list items.

或者...2

由于我们在 mvx 中没有任何 RelativeSource 绑定指令,这种类型的重定向可以在 ViewModel/Model 代码中完成.

Since we don't have any RelativeSource binding instructions in mvx, this type of redirection can be done in the ViewModel/Model code.

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

This can be done by presenting a behaviour-enabled wrapper of the Model object rather than the Model object itself - e.g. using a 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 将不得不使用如下绑定:

Your axml would then have to use bindings like:

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

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

这种方法的一个例子是会议示例,它使用 WithCommand

One example of this approach is the Conference sample which uses WithCommand

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

However... please note that when using WithCommand<T> we discovered a memory leak - basically the GarbageCollection refused to collect the embedded MvxRelayCommand - which is why WithCommand<T> is IDisposable and why BaseSessionListViewModel clears the list and disposes the WithCommand elements when views are detached.

评论后更新:

如果您的数据列表很大 - 并且您的数据是固定的(您的文章是没有 PropertyChanged 的​​模型)并且您不想招致创建大型 List 的开销,那么解决这个问题的方法可能是使用 WrappingList 类.

If your data list is large - and your data is fixed (your articles are models without PropertyChanged) and you don't want to incur the overhead of creating a large List<WrappedArticle> then one way around this might be to use a WrappingList<T> class.

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

This is very similar to the approach taken in Microsoft code - e.g. in virtualizing lists in WP7/Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

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

For your articles this might be:

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
}   

这篇关于MVVMCross 在 MvxBindableListView 中更改 ViewModel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆