数据绑定 - BindingList, BindingSource 和业务对象:第 2 部分






4.50/5 (8投票s)
数据绑定 - 搜索和排序 BindingList。
引言
继我关于 第一篇 BindingSource
和 BindingList
的文章之后,我计划第二部分也写得非常简单易懂。在许多应用程序中,我们使用自定义业务实体和 BindingSource
来获得数据绑定的好处。现在,当需要排序和搜索功能时,问题就来了,因为 BindingList
默认不提供这些功能。因此,在本文中,我将展示一种在 BindingSource
中实现排序和搜索的简单方法(我建议初学者也看看 MSDN,我也经常这样做)。
背景
我将继续使用相同的 Employee-Employee 详细信息的示例来阐述排序和搜索。您可以从上面的链接下载代码来查看实现。
现在,对于每个业务实体,我们都有一个从 BindingList
继承的集合类;例如,对于 EmployeeBE
,我们有 EmployeeBEList : BindingList<EmployeeBE>
。我决定使用一个通用的 BindingList
类,该类将具有排序和搜索功能。所有这些集合类都将继承通用 BindingList
类。因此,在整个项目中,您只需要一个排序和搜索的实现。这不是代码的正确重用吗?
通用 BindingList
第一步是创建新的通用 BindingList
类。在 BaseBindingList
中,我们将合并排序和搜索功能。
public class BaseBindingList : BindingList
{
}
搜索功能
首先,我们必须重写只读属性 SupportsSearchingCore
,它默认设置为 false
。要启用搜索,此属性应返回 true
。
protected override bool SupportsSearchingCore
{
get{return true;}
}
现在是实现搜索的时候了。为此,我们必须重写 FindCore
方法。通常,将返回与搜索条件匹配的项目的索引。但是,我认为这只有在您拥有唯一项目时才好用。我试图获取与搜索条件匹配的所有项目的索引。为此,我保留了一个 ArrayList
,selectedIndices
,并使用 FindCore
的返回值作为指示符,指示是否找到该项目。
protected override int FindCore(PropertyDescriptor prop, object key)
{
// Get the property info for the specified property.
PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);
T item;
int found = -1;
selectedIndices = new ArrayList();
if (key != null)
{
// Loop through the items to see if the key
// value matches the property value.
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;
}
我们已经完成了搜索的实现,现在我们只需要通过一个方法 Find
来公开这个搜索,以便其他人可以访问它。
public int[] Find(string property, object key)
{
// Check the properties for a property with the specified name.
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
PropertyDescriptor prop = properties.Find(property, true);
// If there is not a match, return -1 otherwise pass search to
// FindCore method.
if (prop == null)
returnIndices = null;
else
{
if (FindCore(prop, key) >= 0)
{
returnIndices = (int[])(selectedIndices.ToArray(typeof(int)));
}
}
return returnIndices;
}
排序功能
现在,我们将在 BaseBindingList
中实现排序功能。首先,重写只读属性 SortPropertyCore
,它默认设置为 false
。通过返回 true
来支持排序。
protected override PropertyDescriptor SortPropertyCore
{
get { return sortPropertyValue; }
}
同样,我们必须重写另一个只读属性以指示列表是否已排序。
private bool isSortedValue;
protected override bool IsSortedCore
{
get { return isSortedValue; }
}
在我们直接开始排序实现之前,需要重写几个只读属性:SortDirectionCore
和 SortPropertyCore
。SortDirectionCore
指示排序的方向,而 SortPropertyCore
是用于对列表进行排序的属性描述符。在这里,您必须记住一件事。要使用的属性应实现 IComparable
接口,该接口具有 CompareTo
方法。这种排序的实现将适用于简单的数据类型,为复杂数据类型的未来增强留下了空间。
private PropertyDescriptor sortPropertyValue;
protected override PropertyDescriptor SortPropertyCore
{
get { return sortPropertyValue; }
}
private ListSortDirection sortDirectionValue;
protected override ListSortDirection SortDirectionCore
{
get { return sortDirectionValue; }
}
我们已经准备好通过重写 ApplySortCore
方法来实现排序。这是实现,在这里我试图跟踪未排序的项目,我将在删除排序时使用它。在排序中,我使用一种简单的交换方法
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortedList = new ArrayList();
// Check to see if the property type we are sorting by implements
// the IComparable interface.
Type interfaceType = prop.PropertyType.GetInterface("IComparable");
if (interfaceType != null)
{
// If so, set the SortPropertyValue and SortDirectionValue.
sortPropertyValue = prop;
sortDirectionValue = direction;
if (!isSortedValue)
{
unsortedList = new ArrayList(this.Count);
}
// Loop through each item, adding it the the sortedItems ArrayList.
foreach (Object item in this.Items)
{
sortedList.Add(prop.GetValue(item));
//Make sure that unsorted list keeps the original
//value when sorting is applied for the first time
if (!isSortedValue)
{
unsortedList.Add(item);
}
}
// Call Sort on the ArrayList.
sortedList.Sort();
// Check the sort direction and then copy the sorted items
// back into the list.
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;
// Raise the ListChanged event so bound controls refresh their
// values.
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
else
// If the property type does not implement IComparable, let the user
// know.
throw new NotSupportedException("Cannot sort by " + prop.Name +
". This" + prop.PropertyType.ToString() +
" does not implement IComparable");
}
此排序方法需要公开。这是执行此操作的代码
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.");
}
我们几乎完成了实现,除了删除排序和添加新项目。要删除排序,您需要使用在排序方法中保留的未排序列表填充列表。在这里,我想将初始列表存储为未排序列表。我已使用此实现重写了 RemoveSortCore
方法。此删除排序是通过 RemoveSort
方法完成的。
如果您有一个已排序的列表,在添加新项目时,需要确保该项目放置在正确的位置。因此,我重写了 EndNew
方法,以便将新添加的项目正确地放置在已排序的列表中。完整的实现可以在附带的代码中找到。
protected override void RemoveSortCore()
{
// Ensure the list has been sorted.
if (unsortedList != null)
{
// Loop through the unsorted items and reorder the
// list per the unsorted list.
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)
{
// Check to see if the item is added to the end of the list,
// and if so, re-sort the list.
if (sortPropertyValue != null && itemIndex == this.Count - 1)
ApplySortCore(this.sortPropertyValue, this.sortDirectionValue);
base.EndNew(itemIndex);
}
历史
本文是我之前关于 BindingSource
、BindingList
的文章的下一部分。如果您不熟悉 BindingList
和 BindingSource
,请阅读本文的 第 1 部分。