65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (8投票s)

2009 年 6 月 27 日

CPOL

4分钟阅读

viewsIcon

68857

downloadIcon

1316

数据绑定 - 搜索和排序 BindingList。

引言

继我关于 第一篇 BindingSourceBindingList 的文章之后,我计划第二部分也写得非常简单易懂。在许多应用程序中,我们使用自定义业务实体和 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 方法。通常,将返回与搜索条件匹配的项目的索引。但是,我认为这只有在您拥有唯一项目时才好用。我试图获取与搜索条件匹配的所有项目的索引。为此,我保留了一个 ArrayListselectedIndices,并使用 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; }
}

在我们直接开始排序实现之前,需要重写几个只读属性:SortDirectionCoreSortPropertyCoreSortDirectionCore 指示排序的方向,而 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);
}

历史

本文是我之前关于 BindingSourceBindingList 的文章的下一部分。如果您不熟悉 BindingListBindingSource,请阅读本文的 第 1 部分

© . All rights reserved.