1
votes

I have a ListBox whose DataSource is a BindingSource. That BindingSource's DataSource is a custom class I've written that implements IList<T>. When the user enters data for a new item in the list, I update the BindingSource by calling BindingSource.Insert() with the new item. This adds the item to the ListBox, but it does not seem to modify the underlying list that is the DataSource for the BindingSource.

When I try to do the same thing with a plain List<T> rather than my custom IList class (FilteredList<T>), the list gets updated by the BindingSource. So the problem seems to lie with my custom class. But I've put breakpoints on every method in FilteredList<T>, and none of them are getting called besides the constructor. So I'm stumped.

Here, for reference, is the code for FilteredList<T>:

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

namespace AmesView.Encapsulation
{
    public class FilteredList<T>: IList<T>
    {
        public delegate bool TestMethod<T>(T item);

        private IList<T> _innerList;

        private TestMethod<T> _test;

        public FilteredList(IList<T> innerList, TestMethod<T> test)
        {
            if (innerList == null)
            {
                throw new ArgumentException("innerList must not be null");
            }
            if (test == null)
            {
                throw new ArgumentException("test must not be null");
            }
            _innerList = innerList;
            _test = test;
        }

        public int IndexOf(T item)
        {
            int count = 0;
            foreach (T tmp in _innerList)
            {
                if (_test(tmp))
                {
                    if (item.Equals(tmp))
                    {
                        break;
                    }
                    count++;
                }
            }
            return count;
        }

        public void Insert(int index, T item)
        {
            int count = 0;
            int allidx = 0;
            foreach (T tmp in _innerList)
            {
                if (_test(tmp))
                {
                    if (count == index)
                    {
                        _innerList.Insert(allidx, item);
                        return;
                    }
                    count++;
                }
                allidx++;
            }
        }

        public void RemoveAt(int index)
        {
            int count = 0;
            int allidx = 0;
            foreach (T tmp in _innerList)
            {
                if (_test(tmp))
                {
                    if (count == index)
                    {
                        _innerList.RemoveAt(allidx);
                        return;
                    }
                    count++;
                }
                allidx++;
            }
        }

        public T this[int index]
        {
            get
            {
                int count = 0;
                int allidx = 0;
                foreach (T tmp in _innerList)
                {
                    if (_test(tmp))
                    {
                        if (count == index)
                        {
                            return _innerList[allidx];
                        }
                        count++;
                    }
                    allidx++;
                }
                return default(T);
            }
            set
            {
                int count = 0;
                int allidx = 0;
                foreach (T tmp in _innerList)
                {
                    if (_test(tmp))
                    {
                        if (count == index)
                        {
                            _innerList[allidx] = value;
                        }
                        count++;
                    }
                    allidx++;
                }
            }
        }

        public void Add(T item)
        {
            _innerList.Add(item);
        }

        public void Clear()
        {
            _innerList.Clear();
        }

        public bool Contains(T item)
        {
            foreach (T tmp in _innerList)
            {
                if (tmp.Equals(item) && _test(tmp))
                {
                    return true;
                }
            }
            return false;
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            int count = 0;
            foreach (T tmp in _innerList)
            {
                if (_test(tmp))
                {
                    int idx = arrayIndex + count;
                    if (idx < array.Length)
                    {
                        array[idx] = tmp;
                    }
                    count++;
                }
            }
        }

        public int Count
        {
            get
            {
                int count = 0;
                foreach (T tmp in _innerList)
                {
                    if (_test(tmp))
                    {
                        count++;
                    }
                }
                return count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return _innerList.IsReadOnly;
            }
        }

        public bool Remove(T item)
        {
            return _innerList.Remove(item);
        }

        public IEnumerator<T> GetEnumerator()
        {
            foreach (T tmp in _innerList)
            {
                if (_test(tmp))
                {
                    yield return tmp;
                }
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            foreach (T tmp in _innerList)
            {
                if (_test(tmp))
                {
                    yield return tmp;
                }
            }
        }
    }
}
1

1 Answers

0
votes

This appears to be the intended behavior of the BindingSource class. See the Remarks section in Microsoft's documentation on the BindingSource.DataSource property:

http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.datasource%28v=vs.110%29.aspx

Because the DataSource can be set to many different types of objects, there is custom behavior for many types. The behavior for a DataSource of type List<T> is apparently to wrap the List<T> in a BindingList<T>, whereas the behavior for other classes that implement IEnumerable<T> is to create a new BindingList<T> into which the items from the IEnumerable<T> are copied. Because my FilteredList<T> class did not inherit from List<T>, it got the "less special treatment" and was handled as a generic IEnumerable<T> rather than a List<T>. Unfortunate, for my purposes, but understandable.

Since coming to this conclusion, I have stumbled on another useful bit of information: if you create a BindingList<T> yourself, passing an IList<T> to the constructor, then assign this BindingList<T> to the DataSource property of your BindingSource, everything works and changes made to the BindingSource are reflected in the original list.