Introduction
After my first article on BindingSource
and BindingList
, I was planning
to have the second part also to be very simple and easy. In many applications, we use custom business entities and
BindingSource
to get the benefit of databinding. Now, the problem comes
while needing sorting and searching features which do not come with BindingList
by default. So in this article, I would show an easy way to implement
sorting and searching in BindingSource
(I would suggest beginners have a look at MSDN also, which I do very frequently).
Background
I would continue with the same example of Employee-Employee details to elaborate sorting and searching. You can download the code from the above link to look into the implementation.
Now, for each of the business entities, we are having a collection class inherited from BindingList
; like for EmployeeBE
, we have
EmployeeBEList : BindingList<EmployeeBE>
. I have decided to have a generic BindingList
class which would have
the sorting and searching features. All these collection classes would inherit the generic BindingList
class. So in the whole project,
you only need one implementation of sorting and searching. Is not that proper re-usability of code?
Generic BindingList
The first step is to create the new generic BindingList
class. In BaseBindingList
, we will incorporate the sorting and searching features.
public class BaseBindingList : BindingList
{
}
Searching Feature
First, we have to override the read-only property SupportsSearchingCore
which is set to false
by default. To have searching enabled, this should return true
.
protected override bool SupportsSearchingCore
{
get{return true;}
}
Now it's time to implement searching. For that, we have to override the FindCore
method.
In general, you would find that the index of the item matching the search criteria is returned. But, I felt this is only good if you have unique items.
I tried to get the indices of all the items matching the search criteria. For that, I kept an ArrayList
, selectedIndices
, and used
the FindCore
return value as an indicator of whether the item(s) was found or not.
protected override int FindCore(PropertyDescriptor prop, object key)
{
PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);
T item;
int found = -1;
selectedIndices = new ArrayList();
if (key != null)
{
for (int i = 0; i < Count; ++i)
{
item = (T)Items[i];
if (propInfo.GetValue(item, null).Equals(key))
{
found = 0;
selectedIndices.Add(i);
}
}
}
return found;
}
We are done with the implementation of searching, and now we have to just expose this search with a method Find
so that rest of the world can access this.
public int[] Find(string property, object key)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
PropertyDescriptor prop = properties.Find(property, true);
if (prop == null)
returnIndices = null;
else
{
if (FindCore(prop, key) >= 0)
{
returnIndices = (int[])(selectedIndices.ToArray(typeof(int)));
}
}
return returnIndices;
}
Sorting Feature
We will now implement the sorting feature in BaseBindingList
. First, override the read-only property
SortPropertyCore
which is set to false
by default. Sorting is supported by returning true
.
protected override PropertyDescriptor SortPropertyCore
{
get { return sortPropertyValue; }
}
In the same manner, we have to override another read-only property to indicate whether the list is sorted or not.
private bool isSortedValue;
protected override bool IsSortedCore
{
get { return isSortedValue; }
}
Before we directly start the sorting implementation, there are couple of read-only properties which needs
to be overridden: SortDirectionCore
and SortPropertyCore
. SortDirectionCore
indicates the direction
of sorting, and SortPropertyCore
is the property descriptor used for sorting the list. Here, you have to keep one thing in mind.
The property to be used should implement the IComparable
interface which has the CompareTo
method. This implementation of sorting
would work for simple datatypes, leaving a scope for future enhancements for complex datatypes.
private PropertyDescriptor sortPropertyValue;
protected override PropertyDescriptor SortPropertyCore
{
get { return sortPropertyValue; }
}
private ListSortDirection sortDirectionValue;
protected override ListSortDirection SortDirectionCore
{
get { return sortDirectionValue; }
}
We are all set for the sorting implementation by overriding the ApplySortCore
method. Here's the implementation where I have tried to keep track
of the unsorted items as well, which I will use while removing sorting. In sorting, I use a simple swapping approach:
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortedList = new ArrayList();
Type interfaceType = prop.PropertyType.GetInterface("IComparable");
if (interfaceType != null)
{
sortPropertyValue = prop;
sortDirectionValue = direction;
if (!isSortedValue)
{
unsortedList = new ArrayList(this.Count);
}
foreach (Object item in this.Items)
{
sortedList.Add(prop.GetValue(item));
if (!isSortedValue)
{
unsortedList.Add(item);
}
}
sortedList.Sort();
if (direction == ListSortDirection.Descending)
sortedList.Reverse();
for (int i = 0; i < this.Count; i++)
{
int[] selectedIndices = this.Find(prop.Name, sortedList[i]);
if (selectedIndices != null && selectedIndices.Length > 0)
{
foreach (int position in selectedIndices)
{
if (position != i)
{
SwapItems(i, position);
}
}
}
}
isSortedValue = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
else
throw new NotSupportedException("Cannot sort by " + prop.Name +
". This" + prop.PropertyType.ToString() +
" does not implement IComparable");
}
This sorting method needs to be exposed. Here's the code to do that:
public void ApplySort(string property, ListSortDirection direction)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
PropertyDescriptor prop = properties.Find(property, true);
if (prop != null)
ApplySortCore(prop, direction);
else
throw new NotSupportedException("Cannot sort by " + prop.Name +
". This" + prop.Name +
" does not exist.");
}
We are almost done with the implementation except for the removal of sorting and adding a new item. To remove the sorting, you need
to populate the list with the unsorted list which is kept in the sorting method. Here, I wanted to store the initial list as an unsorted list.
I have overridden the RemoveSortCore
method with this implementation. This removal of sorting is done via the RemoveSort
method.
If you have a sorted list, while adding a new item, it needs to be taken care that the item is placed in the proper position.
So, I have overridden the EndNew
method to place the newly added item properly in a sorted list. The whole implementation is available in the attached code.
protected override void RemoveSortCore()
{
if (unsortedList != null)
{
for (int i = 0; i < unsortedList.Count;i++ )
{
this[i] = (T)unsortedList[i];
}
isSortedValue = false;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
public override void EndNew(int itemIndex)
{
if (sortPropertyValue != null && itemIndex == this.Count - 1)
ApplySortCore(this.sortPropertyValue, this.sortDirectionValue);
base.EndNew(itemIndex);
}
History
This article is the next part of my previous article on BindingSource
, BindingList
. Please go through the first part
of this article, Part 1, if you are new to BindingList
and BindingSource
.