MVVMCross changing ViewModel within a MvxBindableListView

后端 未结 1 1153
被撕碎了的回忆
被撕碎了的回忆 2020-11-29 11:20

Little problem with my Android application and I don\'t know how to solve it with MVVM Cross.

Here is my Model

public class Article 
{
    string L         


        
相关标签:
1条回答
  • 2020-11-29 12:03

    Your analysis is definitely correct about where the click event is trying to bind.

    There are two approaches I generally take:

    1. Use ItemClick on the List
    2. Continuing using Click but do some redirection on the ViewModel side.

    So...1

    The Main Menu in the tutorial has a ViewModel a bit like:

    public class MainMenuViewModel
        : MvxViewModel
    {
        public List<T> Items { get; set; }
    
        public IMvxCommand ShowItemCommand
        {
            get
            {
                return new MvxRelayCommand<T>((item) => /* do action with item */ );
            }
        }
    }
    

    This is used in axml as:

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

    This approach can only be done for ItemClick on the whole list item - not on individual subviews within the list items.


    Or...2

    Since we don't have any RelativeSource binding instructions in mvx, this type of redirection can be done in the ViewModel/Model code.

    This can be done by presenting a behaviour-enabled wrapper of the Model object rather than the Model object itself - e.g. using a 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; } } 
    }
    

    Your axml would then have to use bindings like:

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

    and

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

    One example of this approach is the Conference sample which uses WithCommand

    However... please note that when using WithCommand<T> we discovered a memory leak - basically the GarbageCollection refused to collect the embedded MvxRelayCommand - which is why WithCommand<T> is IDisposable and why BaseSessionListViewModel clears the list and disposes the WithCommand elements when views are detached.


    Update after comment:

    If your data list is large - and your data is fixed (your articles are models without PropertyChanged) and you don't want to incur the overhead of creating a large List<WrappedArticle> then one way around this might be to use a WrappingList<T> class.

    This is very similar to the approach taken in Microsoft code - e.g. in virtualizing lists in WP7/Silverlight - http://shawnoster.com/blog/post/Improving-ListBox-Performance-in-Silverlight-for-Windows-Phone-7-Data-Virtualization.aspx

    For your articles this might be:

    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 讨论(0)
提交回复
热议问题