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?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
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
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.