2012-10-01 26 views
7

Vấn đề nhỏ với ứng dụng Android của tôi và tôi không biết cách giải quyết nó với MVVM Cross.MVVMCross thay đổi ViewModel trong một MvxBindableListView

Đây là mẫu của tôi

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

My ViewModel

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

} 

layout.axml My ...

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

Và ở đây có vấn đề của tôi, "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'}}" 
      /> 
... 

Lệnh "Nhấp" được gọi là "MyTest" được liên kết trên mục được MvxBindableListView cung cấp. Nói cách khác, nhấp vào tìm kiếm một lệnh "MyTest" trong mô hình "Article" của tôi, thay vì ViewModel của tôi. Làm thế nào tôi có thể thay đổi hành vi đó để liên kết ViewModel của tôi "ArticleViewModel" chịu trách nhiệm về MvxBindableListView của tôi?

Mọi đề xuất?

Trả lời

7

Phân tích của bạn chắc chắn chính xác về nơi sự kiện nhấp đang cố gắng ràng buộc.

Có hai phương pháp tôi thường mất:

  1. Sử dụng ItemClick vào danh sách
  2. Tiếp tục sử dụng Bấm nhưng làm một số chuyển hướng về phía ViewModel.

Vì vậy ... 1

Các Main Menu trong hướng dẫn có một ViewModel một chút như:

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

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

này được sử dụng trong axml như:

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

Cách tiếp cận này chỉ có thể được thực hiện cho ItemClick trên toàn bộ mục danh sách - không trên các bản xem phụ cá nhân trong các mục danh sách.


Hoặc ... 2

Kể từ khi chúng tôi không có bất kỳ hướng dẫn RelativeSource ràng buộc trong mvx, kiểu này chuyển hướng có thể được thực hiện trong các mã ViewModel/Model.

Điều này có thể được thực hiện bằng cách trình bày trình bao bọc hành vi cho phép đối tượng Mô hình thay vì đối tượng Mô hình - ví dụ:sử dụng một 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; } } 
} 

axml của bạn sau đó sẽ phải sử dụng các ràng buộc như:

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

Một ví dụ của phương pháp này là mẫu Hội nghị trong đó sử dụng WithCommand

Tuy nhiên ... xin lưu ý rằng khi sử dụng WithCommand<T> chúng tôi phát hiện rò rỉ bộ nhớ - về cơ bản GarbageCollection từ chối thu thập số MvxRelayCommand được nhúng - đó là lý do tại sao WithCommand<T>IDisposable và tại sao BaseSessionListViewModel xóa danh sách và phân phối các phần tử WithCommand khi chế độ xem được tách ra.


Cập nhật sau khi bình luận:

Nếu danh sách dữ liệu của bạn là lớn - và dữ liệu của bạn là cố định (bài viết của bạn là mô hình mà không PropertyChanged) và bạn không muốn phải chịu các chi phí của việc tạo ra một lớn List<WrappedArticle> thì một cách xung quanh việc này có thể là sử dụng lớp WrappingList<T>.

Điều này rất giống với cách tiếp cận được thực hiện trong mã Microsoft - ví dụ: trong ảo hóa danh sách trong WP7/Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

Đối với bài viết của bạn này có thể là:

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

Trong thực tế, tôi có 4 imagebuttons với hành vi trên listview của tôi. Vì vậy, giải pháp đầu tiên là không thể. Đối với vấn đề thứ hai, vấn đề của tôi là tôi xem lại danh sách của mình từ một khung công tác khác (phân vùng) và phân tích toàn bộ danh sách để tạo một danh sách mới với "ActiveArticle" sẽ đặt một chi phí nghiêm trọng (tôi có thể có hàng nghìn mục). Nhưng dù sao, tôi sẽ thử giải pháp này và cho bạn biết nếu nó hoạt động. Nhân tiện, bạn có nghĩ rằng sẽ khó tạo ra một "RelativeSource" trên khuôn khổ của bạn? Cảm ơn! – hugoterelle

+0

Đã thêm một giải pháp thay thế cho bạn - hãy xem cập nhật trong câu trả lời. Đối với "Nhân tiện, bạn không nghĩ rằng nó sẽ khó" nó sẽ đòi hỏi một số cách để xác định người thân trên tất cả các nền tảng (ví dụ: nó có thể phải sử dụng một số cách điều hướng hệ thống phân cấp cha trong Droid) và sau đó xác định thuộc tính nào để liên kết với (chế độ xem gốc của Droid không có DataContext DependencyObject thuận tiện). Nó có thể là có thể - sẽ được quan tâm để xem những gì bạn muốn - cảm thấy tự do để dự thảo những gì giải pháp axml lý tưởng của bạn có thể trông giống như một vấn đề trong github/slodge/mvvmcross – Stuart

+0

Hi Stuart, bạn giải pháp mới là giải pháp cho tôi. Cảm ơn rất nhiều. Tôi đặt giải pháp của bạn trong mã của tôi, và tôi sẽ đặt "tầm nhìn lý tưởng axml" (đối với tôi) như là một vấn đề trong github. Cảm ơn một lần nữa, bạn là tuyệt vời! – hugoterelle

Các vấn đề liên quan