MVVMCross Cambiare ViewModel all'interno di un mvxbindablelistview
-
12-12-2019 - |
Domanda
Poco problema con la mia applicazione Android e non so come risolverlo con MVVM Cross.
Ecco il mio modello
public class Article
{
string Label{ get; set; }
string Remark { get; set; }
}
.
My ViewModel
public class ArticleViewModel: MvxViewModel
{
public List<Article> Articles;
....
}
.
il mio 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>
...
.
Ed ecco il mio problema, il "articolo_rowtrayout"
...
<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'}}"
/>
...
.
Il comando "Fare clic" chiamato "MyTest" è collegato all'elemento dato da MVXBindableListView.In altre parole, fai clic su Cerca un comando "MyTest" nel mio modello "Articolo", invece del mio mirino.Come posso cambiare quel comportamento per collegare il mio ViewModel "ArticleviewModel" che è responsabile della mia MVXBindableListView?
Qualche suggerimento?
Soluzione
La tua analisi è decisamente corretta su dove l'evento Click sta cercando di legare.
Ci sono due approcci che generalmente prendo:
- .
- Usa ItemClick sull'elenco
- continuando a usare click ma fai un po 'di reindirizzamento sul lato viewModel.
.SO ... 1
the Menu principale nel tutorial ha un punto di vista un po 'come:
.public class MainMenuViewModel : MvxViewModel { public List<T> Items { get; set; } public IMvxCommand ShowItemCommand { get { return new MvxRelayCommand<T>((item) => /* do action with item */ ); } } }
Questo è usato in AXML come:
.<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" />
Questo approccio può essere fatto solo per ItemClick sull'elemento di elenco - non su singoli sottoviews all'interno dell'elenco elementi.
.o ... 2
Dal momento che non abbiamo le istruzioni di rilegatura
RelativeSource
in MVX, questo tipo di reindirizzamento può essere eseguito nel viewModel / codice modello.Questo può essere fatto presentando un wrapper abilitato al comportamento dell'oggetto modello piuttosto che l'oggetto modello stesso - ad es. Utilizzo di un
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; } } }
L'AXML dovrebbe quindi utilizzare Bindings come:
.<TextView ... local:MvxBind="{'Text':{'Path':'TheArticle.Label'}}" />
e
.<ImageButton ... local:MvxBind="{'Click':{'Path':'TheCommand.MyTest'}}" />
Un esempio di questo approccio è il campione della conferenza che utilizza withcommand
Tuttavia ... Si prega di notare che quando si utilizza
WithCommand<T>
abbiamo scoperto una perdita di memoria - fondamentalmente la GarbageCollection ha rifiutato di raccogliere ilMvxRelayCommand
incorporato - motivo per cuiWithCommand<T>
èIDisposable
e perché BaseSessionListViewModel Cancella l'elenco e dispiega gli elementi con i commenti sono distaccati.
.Aggiornamento dopo il commento:
Se il tuo elenco dei dati è grande - e i tuoi dati sono fissi (i tuoi articoli sono modelli senza proprietà) e non si desidera sostenere il sovraccarico di creare un grande
List<WrappedArticle>
, allora un modo in cui potrebbe essere quello di utilizzare una classeWrappingList<T>
.Questo è molto simile all'approccio preso in Codice Microsoft - E.G. in elenchi di virtualizzazione in WP7 / Silverlight - http://shawnoster.com/blog/post/improving-ListBog-performance-in-silverlight-for-windows-phone-7-Data-virtualizzazione.aspx
Per i tuoi articoli questo potrebbe essere:
.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 }