Tony Valenti
Tony Valenti

Reputation: 39

Linking ObservableCollection and a List<T>

I am new to databinding in WPF and could use a little help.

A have a regular class that has a List member. I need to show a ListBox that contains all of those items and updates whenever an item is added to that member.

What is the right process for me to do that? I have tried binding my ListBox to an ObservableCollection. This kind of works because whenever I add something to my ObservableCollection, the listbox updates, however, those adds are not persisted down to the List. How do I "bind" an ObservableCollection to a list so that when I add something to the ObservableCollection, I am really adding it to the list?

Thanks in advance!

Upvotes: 3

Views: 3956

Answers (2)

Andrey Polyakov
Andrey Polyakov

Reputation: 233

ObservableCollection have constructor which takes IEnumerable

ObservableCollection<int> myCollection = new ObservableCollection<int>(myList);

So you can create a ObservableCollection over your List and operates ONLY on this collection. After all operation done you can convert ObservableCollection to List back.

But much simpler is to replace original List with ObservableCollection and bind to it directly.

If required to change original List, you have to write wrapper wich implements INotifyCollectionChanged.

You can try my code snippet:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace ObservableCollectionListCheck
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<string>();
            var oc = new ObservableWrapper<string>(list);
            oc.CollectionChanged += OnCollectionChanged;
            
            list.AddRange(new [] {"1","2","3"});
            
            Console.WriteLine($"List: {string.Join(" ",list)}");            
            Console.WriteLine($"ObservableWrapper: {string.Join(" ", oc)}");

            oc.Add("4");

            Console.WriteLine($"List: {string.Join(" ", list)}");
            Console.WriteLine($"ObservableWrapper: {string.Join(" ", oc)}");

            list.Add("5"); // OnCollectionChanged will not call

            Console.WriteLine($"List: {string.Join(" ", list)}");
            Console.WriteLine($"ObservableWrapper: {string.Join(" ", oc)}");

            Console.ReadLine();
        }

        private static void OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
        {
            Console.WriteLine($"OnCollectionChanged:{notifyCollectionChangedEventArgs.Action}");
        }
    }

    public class ObservableWrapper<T> : IList<T>, INotifyCollectionChanged
    {
        private readonly IList<T> _internalList;

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        private void RaiseCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            CollectionChanged?.Invoke(sender, args);
        }

        public ObservableWrapper(IList<T> list)
        {
            _internalList = list;
        }
        
        public IEnumerator<T> GetEnumerator()
        {
            return _internalList.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void Add(T item)
        {
            _internalList.Add(item);
            RaiseCollectionChanged(this,new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,item));
        }

        public void Clear()
        {
            _internalList.Clear();
            RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public Boolean Contains(T item)
        {
            return _internalList.Contains(item);
        }

        public void CopyTo(T[] array, Int32 arrayIndex)
        {
            _internalList.CopyTo(array,arrayIndex);
        }

        public Boolean Remove(T item)
        {
            var result = _internalList.Remove(item);
            RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
            return result;
        }

        public Int32 Count => _internalList.Count;


        public Boolean IsReadOnly => false;

        public Int32 IndexOf(T item) => _internalList.IndexOf(item);
        
        public void Insert(Int32 index, T item)
        {
            _internalList.Insert(index,item);
            RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,item,index));
        }

        public void RemoveAt(Int32 index)
        {
            _internalList.RemoveAt(index);
            RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _internalList[index], index));
        }

        public T this[Int32 index]
        {
            get { return _internalList[index]; }
            set { _internalList[index] = value; }
        }
    }
}

Upvotes: 6

ironstone13
ironstone13

Reputation: 3453

There is no way to "bind" ObservableCollection to a List, since "bind" is not a part of IList contract. From a "design by contract perspective", there are two interfaces that declare that you will get the notifications on object changes - INotifyPropertyChanged and INotifyCollectionChanged, and List does not implement them. You could implement them yourself, but there is no sense to reinvent the wheel, so it's better to use ObservableCollection directly instead of list, as others suggested.

Upvotes: 2

Related Questions