2012-10-01 43 views
7

Pequeño problema con mi aplicación Android y no sé cómo resolverlo con MVVM Cross.MVVMCross cambia ViewModel dentro de una MvxBindableListView

Aquí es mi modelo

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

Mi modelo de vista

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

} 

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

Y aquí viene mi problema, el "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'}}" 
      /> 
... 

El comando "Hacer clic" llamado "MyTest" está vinculado al elemento proporcionado por MvxBindableListView. En otras palabras, haga clic en buscar un comando "MyTest" en mi modelo "Artículo", en lugar de mi ViewModel. ¿Cómo puedo cambiar ese comportamiento para vincular mi ViewModel "ArticleViewModel" que es responsable de mi MvxBindableListView?

¿Alguna sugerencia?

Respuesta

7

Su análisis es definitivamente correcto acerca de dónde está intentando enlazar el evento click.

hay dos enfoques que generalmente toma:

  1. Uso ItemClick en la Lista
  2. Continuando con el uso de clic, pero hacer algo de redirección en el lado ViewModel.

Así que ... 1

El Main Menu en el tutorial tiene un modelo de vista un poco como:

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

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

Esto se utiliza en axml como:

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

Este enfoque solo se puede hacer para ItemClick en todo el elemento de la lista, no en subvistas individuales dentro de los elementos de la lista.


O ... 2

Ya que no tenemos RelativeSource instrucciones vinculantes en mvx, este tipo de redirección puede hacerse en el código modelo de vista/modelo.

Esto se puede hacer presentando un envoltorio habilitado para el comportamiento del objeto Modelo en lugar del objeto Modelo en sí, p. Ej.utilizando 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; } } 
} 

Su axml entonces tendría que utilizar fijaciones como:

y

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

Un ejemplo de este enfoque es la muestra de la Conferencia, que utiliza WithCommand

Sin embargo ... tenga en cuenta que al usar WithCommand<T> descubrimos una pérdida de memoria: básicamente, GarbageCollection se negó a recopilar el MvxRelayCommand incrustado, por lo que WithCommand<T> es IDisposable y por qué BaseSessionListViewModel borra la lista y elimina los elementos de WithCommand cuando se quitan las vistas.


Update después de comentario:

Si su lista de datos es grande - y sus datos es fijo (sus artículos son modelos sin PropertyChanged) y no quiere incurrir en la sobrecarga de crear una grande List<WrappedArticle> entonces una forma de evitar esto podría ser utilizar una clase WrappingList<T>.

Esto es muy similar al enfoque adoptado en el código de Microsoft, p. Ej. en la virtualización de las listas en WP7/Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

para sus artículos que esto podría ser:

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

De hecho, tengo 4 imagebuttons con el comportamiento de mi vista de lista. Entonces la primera solución es imposible. Para el segundo, mi problema es que revelé mi lista desde otro framework (vici coolstorage), y analizar toda la lista para crear una nueva con "ActiveArticle" representará una carga importante (puedo tener varios miles de elementos). Pero de todos modos, probaré esta solución y te dejaré saber si funciona. Por cierto, ¿no crees que será difícil crear un "RelativeSource" en tu framework? ¡Gracias! – hugoterelle

+0

Han agregado una alternativa para usted - vea la actualización en la respuesta. Para "Por cierto, ¿no crees que será difícil?" Requeriría alguna forma de identificar el origen de todas las plataformas (por ejemplo, podría tener que usar alguna forma de navegar la jerarquía principal en Droid) y luego identificar qué propiedad para enlazar (las vistas nativas de Droid no tienen un conveniente DataContext DependencyObject). Podría ser posible, estaría interesado en ver lo que le gustaría, siéntase libre de redactar cómo podría verse su solución axml ideal como un problema en github/slodge/mvvmcross – Stuart

+0

Hola Stuart, tu nueva solución es LA solución para mí. Muchas gracias. Puse su solución en mi código, y pondré la "visión axml ideal" (para mí) como un problema en github. ¡Gracias de nuevo, eres genial! – hugoterelle

Cuestiones relacionadas