How do you create a sortable and searchable strongly typed custom collection?

I have been trying to put together a custom collection that is strongly typed and is also sortable and searchable.

I have been able to create a collection that is sortable but unable to create one that is searchable via BindingSource.Find().

I would like to be able to set the DataSource property of a BindingSource to my custom collection and be able to set the Sort property of the BindingSource and also use the Find(string, object) method to search the collection.

I don't consider myself to be stupid but this custom collection stuff is a bit out of my grasp and I need some guidance.

I would appreciate some simple straight forward examples (if anyone has one).

Thanks.
LVL 3
jeshbrAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
JimBrandleyConnect With a Mentor Commented:
You need to implement the IBindingList interface:

ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpref3/html/T_System_ComponentModel_IBindingList.htm

Specifically, the Find() method. It is unfortunately not very clearly explained in the documentation. I will see if I can locate a better example than that shown in the on-line help.

Jim
0
 
jeshbrAuthor Commented:
This is a simple collection that I will start with.
I understand all that is being done here.
Where do I need to go from here?
using System;
using System.Collections.Generic;
using System.Text;
 
namespace SortableBindingListTest
{
    public interface IPart
    {
        string PartNumber { get; set; }
        string Description { get; set; }
        string PartType { get; set; }
        string Notes { get; set; }
    }
}
 
 
using System;
using System.Collections.Generic;
using System.Text;
 
namespace SortableBindingListTest
{
    public class Part : 
        IPart
    {
        private string _PartNumber;
        private string _Description;
        private string _PartType;
        private string _Notes;
        
        #region IPart Members
 
        public string PartNumber
        {
            get
            {
                return this._PartNumber;
            }
            set
            {
                this._PartNumber = value;
            }
        }
 
        public string Description
        {
            get
            {
                return this._Description;
            }
            set
            {
                this._Description = value;
            }
        }
 
        public string PartType
        {
            get
            {
                return this._PartType;
            }
            set
            {
                this._PartType = value;
            }
        }
 
        public string Notes
        {
            get
            {
                return this._Notes;
            }
            set
            {
                this._Notes = value;
            }
        }
 
        #endregion
    }
}
 
 
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
 
namespace SortableBindingListTest
{
    public class PartCollection :
        IList<IPart>,
        ICollection<IPart>,
        IEnumerable<IPart>
    {
        IList<IPart> _Items = new List<IPart>();
 
        #region IList<IPart> Members
 
        public int IndexOf(IPart item)
        {
            return this._Items.IndexOf(item);
        }
 
        public void Insert(int index, IPart item)
        {
            this._Items.Insert(index, item);
        }
 
        public void RemoveAt(int index)
        {
            this._Items.RemoveAt(index);
        }
 
        public IPart this[int index]
        {
            get
            {
                return this._Items[index];
            }
            set
            {
                this._Items[index] = value;
            }
        }
 
        #endregion
 
        #region ICollection<IPart> Members
 
        public void Add(IPart item)
        {
            this._Items.Add(item);
        }
 
        public void Clear()
        {
            this._Items.Clear();
        }
 
        public bool Contains(IPart item)
        {
            return this._Items.Contains(item);
        }
 
        public void CopyTo(IPart[] array, int arrayIndex)
        {
            this._Items.CopyTo(array, arrayIndex);
        }
 
        public int Count
        {
            get { return this._Items.Count; }
        }
 
        public bool IsReadOnly
        {
            get { return false; }
        }
 
        public bool Remove(IPart item)
        {
            return this._Items.Remove(item);
        }
 
        #endregion
 
        #region IEnumerable<IPart> Members
 
        public IEnumerator<IPart> GetEnumerator()
        {
            return this._Items.GetEnumerator();
        }
 
        #endregion
 
        #region IEnumerable Members
 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this._Items.GetEnumerator();
        }
 
        #endregion
    }
}

Open in new window

0
 
JimBrandleyCommented:
I looked at a lot of sites - found two that said they had links to code that worked - both links were bad.

Jim
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
jeshbrAuthor Commented:
I have tried to implement IBindingList but all of the examples that I have found do not support sorting or searching.
0
 
jeshbrAuthor Commented:
I got it.

I was trying to use IBindingList before but I was also inheriting from BindingList<T>.
There must be some problems doing that.

With some trial and error I kinda figured out how PropertyDescriptors work.
And I found a nice class for the IComparer needed for the ApplySort method.
Then I realized that I could use the List<T> sort method if I created a predicate method.
So that is what I did.

And finally I modified it to use a generic to I can reuse it.

Do you see any problems with what I did?
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections;
 
namespace SortableBindingListTest
{
    public class SortableSearchableList<T> :
        IList<T>,
        IBindingList,
        IComponent,
        ITypedList
    {
        private IList<T> _Items = new List<T>();
        private bool _IsReadOnly = false;
        private bool _IsSorted = false;
 
        private PropertyDescriptorCollection _Properties = new PropertyDescriptorCollection(new PropertyDescriptor[] { }, false);
 
        private ListSortDirection _SortDirection = ListSortDirection.Ascending;
        private PropertyDescriptor _SortProperty = null;
        private ISite _Site = null;
        private bool _AllowEdit = true;
        private bool _AllowNew = true;
        private bool _AllowRemove = true;
        private bool _SupportsSearching = true;
        private bool _SupportsSorting = true;
        private bool _IsFixedSize = false;
        private bool _IsSynchronized = true;
        private object _SyncRoot = new object();
 
        private static object _SearchKey = null;
        private static PropertyDescriptor _SearchProperty = null;
 
        private static bool SearchCollection(T item)
        {
            if (_SearchProperty == null)
            {
                return false;
            }
 
            if (_SearchKey == null)
            {
                return false;
            }
 
            object value = _SearchProperty.GetValue(item);
            object searchValue = Convert.ChangeType(_SearchKey, _SearchProperty.PropertyType);
 
            if (value.Equals(searchValue))
            {
                return true;
            }
 
            return false;
        }
 
        #region IList<T> Members
 
        public int IndexOf(T item)
        {
            return this._Items.IndexOf(item);
        }
 
        public void Insert(int index, T item)
        {
            this._Items.Insert(index, item);
        }
 
        public void RemoveAt(int index)
        {
            this._Items.RemoveAt(index);
        }
 
        public T this[int index]
        {
            get
            {
                return this._Items[index];
            }
            set
            {
                this._Items[index] = value;
            }
        }
 
        #endregion
 
        #region ICollection<T> Members
 
        public void Add(T item)
        {
            this._Items.Add(item);
        }
 
        public void Clear()
        {
            this._Items.Clear();
        }
 
        public bool Contains(T item)
        {
            return this._Items.Contains(item);
        }
 
        public void CopyTo(T[] array, int arrayIndex)
        {
            this._Items.CopyTo(array, arrayIndex);
        }
 
        public int Count
        {
            get { return this._Items.Count; }
        }
 
        public bool IsReadOnly
        {
            get { return this._IsReadOnly; }
        }
 
        public bool Remove(T item)
        {
            return this._Items.Remove(item);
        }
 
        #endregion
 
        #region IEnumerable<T> Members
 
        public IEnumerator<T> GetEnumerator()
        {
            return this._Items.GetEnumerator();
        }
 
        #endregion
 
        #region IEnumerable Members
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this._Items.GetEnumerator();
        }
 
        #endregion
 
        #region IBindingList Members
 
        public void AddIndex(PropertyDescriptor property)
        {
            this._Properties.Add(property);
        }
 
        public virtual object AddNew()
        {
            T item = (T) Activator.CreateInstance<T>();
            this._Items.Add(item);
            return item;
        }
 
        public bool AllowEdit
        {
            get { return this._AllowEdit; }
        }
 
        public bool AllowNew
        {
            get { return this._AllowNew; }
        }
 
        public bool AllowRemove
        {
            get { return this._AllowRemove; }
        }
 
        public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
        {
            this._IsSorted = true;
            this._SortProperty = property;
            this._SortDirection = direction;
            ((List<T>)this._Items).Sort(new PropertyComparer<T>(this._SortProperty, this._SortDirection));
        }
 
        public int Find(PropertyDescriptor property, object key)
        {
            _SearchProperty = property;
            _SearchKey = key;
            return this._Items.IndexOf(((List<T>)(this._Items)).Find(SearchCollection));
        }
 
        public bool IsSorted
        {
            get { return this._IsSorted; }
        }
 
        public event ListChangedEventHandler ListChanged;
 
        public void RemoveIndex(PropertyDescriptor property)
        {
            this._Properties.Remove(property);
        }
 
        public void RemoveSort()
        {
            this._SortProperty = null;
            this._SortDirection = ListSortDirection.Ascending;
            this._IsSorted = false;
            ((List<T>)this._Items).Sort();
        }
 
        public ListSortDirection SortDirection
        {
            get { return this._SortDirection; }
        }
 
        public PropertyDescriptor SortProperty
        {
            get { return this._SortProperty; }
        }
 
        public bool SupportsChangeNotification
        {
            get { return (this.ListChanged != null); }
        }
 
        public bool SupportsSearching
        {
            get { return this._SupportsSearching; }
        }
 
        public bool SupportsSorting
        {
            get { return this._SupportsSorting; }
        }
 
        #endregion
 
        #region IList Members
 
        public int Add(object value)
        {
            if (value.GetType() != typeof(T))
            {
                throw new ArgumentException(
                    "Unexpected argument type: '" + value.GetType().Name + "' received.\n" +
                    "Expected type: '" + typeof(T).Name + "'"
                    );
            }
 
            this._Items.Add((T)value);
 
            return this._Items.IndexOf((T)value);
        }
 
        public bool Contains(object value)
        {
            if (value.GetType() != typeof(T))
            {
                throw new ArgumentException(
                    "Unexpected argument type: '" + value.GetType().Name + "' received.\n" +
                    "Expected type: '" + typeof(T).Name + "'"
                    );
            }
 
            return this._Items.Contains((T)value);
        }
 
        public int IndexOf(object value)
        {
            if (value.GetType() != typeof(T))
            {
                throw new ArgumentException(
                    "Unexpected argument type: '" + value.GetType().Name + "' received.\n" +
                    "Expected type: '" + typeof(T).Name + "'"
                    );
            }
 
            return this._Items.IndexOf((T)value);
        }
 
        public void Insert(int index, object value)
        {
            if (value.GetType() != typeof(T))
            {
                throw new ArgumentException(
                    "Unexpected argument type: '" + value.GetType().Name + "' received.\n" +
                    "Expected type: '" + typeof(T).Name + "'"
                    );
            }
 
            this._Items.Insert(index, (T)value);
        }
 
        public bool IsFixedSize
        {
            get { return this._IsFixedSize; }
        }
 
        public void Remove(object value)
        {
            if (value.GetType() != typeof(T))
            {
                throw new ArgumentException(
                    "Unexpected argument type: '" + value.GetType().Name + "' received.\n" +
                    "Expected type: '" + typeof(T).Name + "'"
                    );
            }
 
            this._Items.Remove((T)value);
        }
 
        object IList.this[int index]
        {
            get
            {
                return this._Items[index];
            }
            set
            {
                if (value.GetType() != typeof(T))
                {
                    throw new ArgumentException(
                        "Unexpected argument type: '" + value.GetType().Name + "' received.\n" +
                        "Expected type: '" + typeof(T).Name + "'"
                        );
                }
 
                this._Items[index] = (T)value;
            }
        }
 
        #endregion
 
        #region ICollection Members
 
        public void CopyTo(Array array, int index)
        {
            T[] items = new T[this._Items.Count - index];
            int i;
            for (i = index; i < items.Length; i++)
            {
                items[i] = this._Items[i];
            }
            array = items;
        }
 
        public bool IsSynchronized
        {
            get { return this._IsSynchronized; }
        }
 
        public object SyncRoot
        {
            get { return this._SyncRoot; }
        }
 
        #endregion
 
        #region IComponent Members
 
        public event EventHandler Disposed;
 
        public ISite Site
        {
            get
            {
                return this._Site;
            }
            set
            {
                this._Site = value;
            }
        }
 
        #endregion
 
        #region IDisposable Members
 
        public void Dispose()
        {
            if (this.Disposed != null)
            {
                this.Disposed(this, EventArgs.Empty);
            }
        }
 
        #endregion
 
        #region ITypedList Members
 
        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            PropertyDescriptorCollection properties = null;
            PropertyDescriptorCollection typeProperties = TypeDescriptor.GetProperties(typeof(T));
 
            if (null == listAccessors)
            {
                properties = typeProperties;
 
                foreach (PropertyDescriptor propertyDescriptor in this._Properties)
                {
                    if (!properties.Contains(propertyDescriptor))
                    {
                        properties.Add(propertyDescriptor);
                    }
                }
            }
            else
            {
                properties = System.Windows.Forms.ListBindingHelper.GetListItemProperties(listAccessors[0].PropertyType);
            }
 
            return properties;
        }
 
        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return typeof(T).Name;
        }
 
        #endregion
    }
}
 
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Reflection;
 
namespace SortableBindingListTest
{
    public class PropertyComparer<T> : IComparer<T>
    {
        // The following code contains code implemented by Rockford Lhotka:
        // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp
 
        private PropertyDescriptor _property;
        private ListSortDirection _direction;
 
        public PropertyComparer(PropertyDescriptor property, ListSortDirection direction)
        {
            _property = property;
            _direction = direction;
        }
 
        #region IComparer<T>
 
        public int Compare(T x, T y)
        {
            // Get property values
            object xValue = GetPropertyValue(x, _property.Name);
            object yValue = GetPropertyValue(y, _property.Name);
 
            // Determine sort order
            if (_direction == ListSortDirection.Ascending)
            {
                return CompareAscending(xValue, yValue);
            }
            else
            {
                return CompareDescending(xValue, yValue);
            }
        }
 
        public bool Equals(T x, T y)
        {
            return x.Equals(y);
        }
 
        public int GetHashCode(T obj)
        {
            return obj.GetHashCode();
        }
 
        #endregion
 
        // Compare two property values of any type
        private int CompareAscending(object xValue, object yValue)
        {
            int result;
 
            // If values implement IComparer
            if (xValue is IComparable)
            {
                result = ((IComparable)xValue).CompareTo(yValue);
            }
            // If values don't implement IComparer but are equivalent
            else if (xValue.Equals(yValue))
            {
                result = 0;
            }
            // Values don't implement IComparer and are not equivalent, so compare as string values
            else result = xValue.ToString().CompareTo(yValue.ToString());
 
            // Return result
            return result;
        }
 
        private int CompareDescending(object xValue, object yValue)
        {
            // Return result adjusted for ascending or descending sort order ie
            // multiplied by 1 for ascending or -1 for descending
            return CompareAscending(xValue, yValue) * -1;
        }
 
        private object GetPropertyValue(T value, string property)
        {
            // Get property
            PropertyInfo propertyInfo = value.GetType().GetProperty(property);
 
            // Return value
            return propertyInfo.GetValue(value, null);
        }
    }
}

Open in new window

0
 
JimBrandleyCommented:
That looks sweet. It's a pleasure to finally see one without a NotImplementedException. Nice Job.

Jim
0
 
jeshbrAuthor Commented:
Thanks.


And a note:

[Then I realized that I could use the List<T> sort method if I created a predicate method.
So that is what I did.]

I meant to say List<T> Find method not Sort..
0
 
JimBrandleyCommented:
My pleasure. Good luck.

Jim
0
All Courses

From novice to tech pro — start learning today.