MVVMCross changes ViewModel to MvxBindableListView

A little problem with my Android app, and I don't know how to solve it using MVVM Cross.

Here is my model

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

My viewmodel

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

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> ... 

And here is my problem: "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'}}" /> ... 

The Click command, called MyTest, is associated with the item specified by MvxBindableListView. In other words, click “Search” for the “MyTest” command in my “Article” model instead of my ViewModel. How can I change this behavior to bind my ViewModel "ArticleViewModel", which is responsible for my MvxBindableListView?

Any suggestions?

+7
source share
1 answer

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

There are two approaches that I usually do:

  • Use ItemClick in list
  • Continued use Click, but do some redirection on the ViewModel side.

So ... 1

The main menu in the tutorial has a ViewModel a bit similar:

 public class MainMenuViewModel : MvxViewModel { public List<T> Items { get; set; } public IMvxCommand ShowItemCommand { get { return new MvxRelayCommand<T>((item) => /* do action with item */ ); } } } 

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

This approach can only be done for ItemClick in the entire list item - not for individual subzones in list items.


Or 2

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

This can be done by presenting a wrapper with support for the Model object enabled, and not the model object itself - for example, using 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; } } } 

Then your axml will have to use type bindings:

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

and

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

One example of this approach is a sample conference that uses WithCommand

However ... note that when using WithCommand<T> we found a memory leak - basically GarbageCollection refused to collect the built-in MvxRelayCommand - that is why WithCommand<T> is IDisposable and why BaseSessionListViewModel clears the list and creates WithCommand elements when views are disabled.


Update after comment:

If your data list is large - and your data is corrected (your articles are models without PropertyChanged), and you do not want to bear the overhead of creating a large List<WrappedArticle> , then one way could be to use the WrappingList<T> class.

This is very similar to the approach adopted in Microsoft code - for example, in list virtualization in WP7 / Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7 -Data-Virtualization.aspx

For your articles, this could 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 } 
+7
source

All Articles