2012-10-01 10 views
7

Mały problem z moją aplikacją na Androida i nie wiem jak rozwiązać problem z MVVM Cross.MVVMCross zmieniając ViewModel w MvxBindableListView

Oto mój model

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

My ViewModel

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

} 

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

I tu moje problemu, "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'}}" 
      /> 
... 

Komenda "Kliknij" o nazwie "MyTest" jest połączona z elementem podanym przez MvxBindableListView. Innymi słowy, kliknij Wyszukaj polecenie "MyTest" w moim modelu "Artykuł", zamiast mojego ViewModel. Jak mogę zmienić to zachowanie, aby połączyć mój ViewModel "ArticleViewModel", który jest odpowiedzialny za mój MvxBindableListView?

Wszelkie sugestie?

Odpowiedz

7

Twoja analiza zdecydowanie określa miejsce, w którym zdarzenie typu kliknięcie próbuje powiązać.

Istnieją dwa podejścia ja generalnie biorą:

  1. Zastosowanie itemClick na Liście
  2. Kontynuowanie korzystania Kliknij ale zrobić przekierowanie na stronie ViewModel.

Więc ... 1

The Main Menu w tutorialu ma ViewModel trochę jak:

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

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

ta jest stosowana w axml jak:

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

To podejście można wykonać tylko dla ItemClick na całym elemencie listy - nie na poszczególnych subviewsach w elementach listy.


Albo ... 2

Ponieważ nie mamy żadnych RelativeSource wiążących instrukcji w MVX tego typu przekierowania można zrobić w kodzie ViewModel/Model.

Można to zrobić, prezentując otoczkę z zachowaniem obiektu Model, a nie sam obiekt Model - np.stosując 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; } } 
} 

Twój axml będzie wtedy musiał użyć wiązań jak:

i

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

Przykładem takiego podejścia jest próbka konferencji, która wykorzystuje WithCommand

Należy jednak pamiętać, że podczas korzystania z WithCommand<T> wykryliśmy wyciek pamięci - w zasadzie GarbageCollection odmówił odebrania osadzonego MvxRelayCommand - dlatego WithCommand<T> jest IDisposable i dlaczego BaseSessionListViewModel czyści listę i udostępnia elementy WithCommand, gdy widoki są odłączone.


Aktualizacja po komentarzu:

Jeśli lista danych jest duża - a Twoje dane są stałe (artykuły są modele bez PropertyChanged) i nie chcą ponosić napowietrznej tworząc duże List<WrappedArticle> to jednym ze sposobów obejścia tego może być użycie klasy WrappingList<T>.

Jest to bardzo podobne do podejścia przyjętego w kodzie Microsoft - np. w wirtualizacji list w WP7/Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

dla Twoich artykułów może to być:

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 
} 
+0

W rzeczywistości, mam 4 imagebuttons z zachowania na moim ListView. Tak więc pierwsze rozwiązanie jest niemożliwe. Po drugie, moim problemem jest to, że przeglądam moją listę z innego frameworka (vici coolstorage), i parsowanie całej listy w celu stworzenia nowej z "ActiveArticle" spowoduje poważne obciążenie (mogę mieć kilka tysięcy pozycji). Ale w każdym razie spróbuję tego rozwiązania i dam znać, czy to działa. A propos, nie sądzisz, że trudno będzie stworzyć "RelativeSource" w twojej strukturze? Dzięki! – hugoterelle

+0

Dodano opcję dla Ciebie - zobacz aktualizację w odpowiedzi. Dla "Nawiasem mówiąc, nie sądzisz, że będzie to trudne", to wymagałoby jakiegoś sposobu zidentyfikowania źródła krewnych na wszystkich platformach (np. Może wymagać użycia jakiegoś sposobu poruszania się w hierarchii nadrzędnej w Droid), a następnie określenia, która właściwość powiązać z (widok macierzysty Droid nie ma wygodnego obiektu zależności DataContext). To może być możliwe - chciałbym zobaczyć, co chcesz - możesz zaprojektować swoje idealne rozwiązanie axml jako problem w github/slodge/mvvmcross – Stuart

+0

Witam Stuart, nowe rozwiązanie jest dla mnie rozwiązaniem. Wielkie dzięki. Umieszczam twoje rozwiązanie w moim kodzie, a ja umieszczę "idealną wizję axml" (dla mnie) jako problem w github. Jeszcze raz dziękuję, jesteś świetny! – hugoterelle

Powiązane problemy