Pergunta

Pequeno problema com meu aplicativo Android e não sei como resolver com MVVM Cross.

Aqui está meu modelo

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

Meu ViewModel

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

}

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

E aí vem o meu problema, o "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'}}"
          />
...

O comando "Click" denominado "MyTest" está vinculado ao item fornecido pelo MvxBindableListView.Em outras palavras, clique em procurar um comando "MyTest" no meu modelo "Artigo", em vez do meu ViewModel.Como posso alterar esse comportamento para vincular meu ViewModel “ArticleViewModel” que é responsável pelo meu MvxBindableListView?

Alguma sugestão?

Foi útil?

Solução

Sua análise está definitivamente correta sobre onde o evento click está tentando se vincular.

Existem duas abordagens que geralmente adoto:

  1. Use ItemClick na lista
  2. Continuando usando Click mas faça algum redirecionamento no lado do ViewModel.

Então...1

O Menu principal no tutorial tem um ViewModel um pouco como:

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

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

Isso é usado em 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"
  />

Essa abordagem só pode ser feita para ItemClick em todo o item da lista - não em subvisualizações individuais nos itens da lista.


Ou...2

Como não temos nenhum RelativeSource instruções de ligação em mvx, esse tipo de redirecionamento pode ser feito no código ViewModel/Model.

Isso pode ser feito apresentando um wrapper habilitado para comportamento do objeto Model em vez do próprio objeto Model - por exemplo.usando um 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; } } 
}

Seu axml teria então que usar ligações como:

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

e

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

Um exemplo desta abordagem é o exemplo da Conferência que utiliza ComComando

No entanto...observe que ao usar WithCommand<T> descobrimos um vazamento de memória - basicamente o GarbageCollection se recusou a coletar os dados incorporados MvxRelayCommand - e é por isso WithCommand<T> é IDisposable e porque BaseSessionListViewModel limpa a lista e descarta os elementos WithCommand quando as visualizações são desanexadas.


Atualização após comentário:

Se sua lista de dados for grande - e seus dados forem fixos (seus artigos são modelos sem PropertyChanged) e você não quiser incorrer na sobrecarga de criar uma lista grande List<WrappedArticle> então uma maneira de contornar isso pode ser usar um WrappingList<T> aula.

Isso é muito semelhante à abordagem adotada no código da Microsoft - por exemplo.na virtualização de listas no WP7/Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

Para seus artigos, isso pode 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
}   
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top