Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model List<T> and ViewModel ObservableCollection<T> Duplicate Data?

What is the best practice for having a List<T> in the business layer that needs to be displayed on a UI? I currently use databinding with an ObservableCollection<T> in the viewmodel that duplicates the data of the List<T>. The obvious problem of this is when the List<T> is modified in the business layer the ObservableCollection<T> needs to be recreated so the changes of the List<T> are reflected in the UI. This can't be the best way.

I also will not accept using an ObservableCollection<T> in the business layer as an answer.

Thanks!

like image 535
ryand Avatar asked Mar 05 '14 15:03

ryand


2 Answers

  1. If you insist on having List<T> and separate events notifying about list modification, then duplication is the only sensible way.

    1. If you have ListChanged event with no details on what was actually changed, you can avoid duplication and just wrap the list in a proxy collection implementing INotifyCollectionChanged interface which will fire appropriate CollectionChanged events in NotifyCollectionChangedAction.Reset mode.

    2. If you have granular ItemChanged, ItemAdded etc. events, then you're effectively duplicating ObservableCollection<T> functionality. In this case, you can wrap your application in a proxy collection implementing INotifyCollectionChanged interface, but which understands your architecture and translates events into appropriate NotifyCollectionChangedAction.

  2. Having ObservableCollection<T> in business layer isn't a bad idea at all. It is a specialized collection which provides common interface to notify about item changes, not some class designed specifically for WinForms or WPF or whatever.

like image 92
Athari Avatar answered Oct 25 '22 15:10

Athari


You can implement the INotifyCollectionChanged interface, but if you want to use it in a way that you can hold on your implementation the collection in case implementing a class of your own also holding an implementation of IEnumerable will do a lot of the work for you a for instance is what follows, this is the base class i use for holding all the collections that will be updated, on this implementation there is also an ordering consideration in the variable _ordering:

public abstract class BaseINotifyCollectionChanged<T, K> : INotifyCollectionChanged, IEnumerable<T>
{
    Func<T, K> _ordering;
    bool _ascending;

    public BaseINotifyCollectionChanged()
    {
    }

    public BaseINotifyCollectionChanged(Func<T, K> ordering, bool ascending = true)
    {
        _ordering = ordering;
        _ascending = ascending;
        OnCollectionChanged();
    }

    protected abstract IList<T> GetCollection();

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    protected void OnCollectionChanged()
    {
        if (CollectionChanged != null)
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    public void RaiseCollectionChanged()
    {
        OnCollectionChanged();
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
                                                                                  GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
                                                                                  GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
    }
}
}

When you have this implementation you can use as you wish and through out all the collections you need on your app, working on some dry for yourself here is one example of the use you can give to this abstract class:

public class Categories : BaseINotifyCollectionChanged<Category, string>
{
    long _onCategoryRoot;
    public void SetOnCategoryRoot(long categoryId)
    {
        _onCategoryRoot = categoryId;
        RaiseCollectionChanged();
    }

    protected override IList<Category> GetCollection()
    {
        Category category = new Category();
        return _onRoot ? category.GetRootCategories() : category.GetSubCategoriesOnRoot(_onCategoryRoot);
    }
}

When you set a _onCategoryRoot in the class the collection you will be displaying will be updated via the RaiseCollectionChanged() method, so you need to add in your viewmodel a property with the class Categories and set the binding in the XAML.

like image 36
aledustet Avatar answered Oct 25 '22 14:10

aledustet