使用业务对象的DataGridView列排序

5

我是这样设置我的DataGridView的:

        jobs = new List<DisplayJob>();

        uxJobList.AutoGenerateColumns = false;
        jobListBindingSource.DataSource = jobs;
        uxJobList.DataSource = jobListBindingSource;

        int newColumn;
        newColumn = uxJobList.Columns.Add("Id", "Job No.");
        uxJobList.Columns[newColumn].DataPropertyName = "Id";
        uxJobList.Columns[newColumn].DefaultCellStyle.Format = Global.JobIdFormat;
        uxJobList.Columns[newColumn].DefaultCellStyle.Font = new Font(uxJobList.DefaultCellStyle.Font, FontStyle.Bold);
        uxJobList.Columns[newColumn].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
        uxJobList.Columns[newColumn].Width = 62;
        uxJobList.Columns[newColumn].Resizable = DataGridViewTriState.False;
        uxJobList.Columns[newColumn].SortMode = DataGridViewColumnSortMode.Automatic;
        :
        :

DisplayJob类的样子如下:

    public class DisplayJob
{
    public DisplayJob(int id)
    {
        Id = id;
    }

    public DisplayJob(JobEntity job)
    {
        Id = job.Id;
        Type = job.JobTypeDescription;
        CreatedAt = job.CreatedAt;
        StartedAt = job.StartedAt;
        ExternalStatus = job.ExternalStatus;
        FriendlyExternalStatus = job.FriendlyExternalStatus;
        ExternalStatusFriendly = job.ExternalStatusFriendly;
        CustomerName = job.Customer.Name;
        CustomerKey = job.Customer.CustomerKey;
        WorkAddress = job.WorkAddress;
        CreatedBy = job.CreatedBy;
        CancelledAt = job.CancelledAt;
        ClosedAt = job.ClosedAt;
        ReasonWaiting = job.ReasonWaiting;
        CancelledBy = job.CancelledBy;
        CancelledReason = job.CancelledReason;
        DisplayCreator = Global.GetDisplayName(CreatedBy);
        ActionRedoNeeded = job.ActionRedoNeeded;
        if (job.Scheme != null)
        {
            SchemeCode = job.Scheme.Code;
        }

    }

    public int Id { get; private set; }
    public string Type { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime? StartedAt { get; private set; }
    public string ExternalStatus { get; private set; }
    public string FriendlyExternalStatus { get; private set; }
    public string ExternalStatusFriendly { get; private set; }
    public string CustomerName { get; private set; }
    public string CustomerKey { get; private set; }
    public string WorkAddress { get; private set; }
    public string CreatedBy { get; private set; }
    public DateTime? CancelledAt { get; private set; }
    public DateTime? ClosedAt { get; private set; }
    public string CancelledBy { get; private set; }
    public string ReasonWaiting { get; private set; }
    public string DisplayCreator { get; private set; }
    public string CancelledReason { get; private set; }
    public string SchemeCode { get; private set; }
    public bool ActionRedoNeeded { get; private set; }
}

然而,列排序无法正常工作。让它正常工作的最佳方法是什么?
8个回答

9
如果您想在集合上支持排序和搜索,您只需要从参数化类型继承一个类,并覆盖一些基类方法和属性即可。最好的方式是扩展BindingList,并执行以下操作:
protected override bool SupportsSearchingCore
{
    get
    {
        return true;
    }
}

protected override bool SupportsSortingCore
{
    get { return true; }
}

您还需要实现排序代码:
ListSortDirection sortDirectionValue;
PropertyDescriptor sortPropertyValue;

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;

        unsortedItems = 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));
            unsortedItems.Add(item);
        }
        // Call Sort on the ArrayList.
        sortedList.Sort();
        T temp;

        // 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 position = Find(prop.Name, sortedList[i]);
            if (position != i) {
                temp = this[i];
                this[i] = this[position];
                this[position] = temp;
            }
        }

        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");
}

如果您需要更多信息,您可以随时前往该网站获取有关如何扩展绑定列表的所有说明。 点击此处

5

Daok的解决方案是正确的,但往往比它值得的更多。

想要获取所需功能的懒人方法是创建并填充一个DataTable,然后将DataGridView绑定到它。

这种方法无法处理许多用例(例如编辑),而且显然浪费时间和空间。正如我所说,这是一种懒惰的方式。

但它很容易编写,生成的代码比实现IBindingList的代码要简单得多。

此外,您已经编写了大量的代码或类似的代码:您编写的用于定义DataTable的代码使您无需编写用于创建DataGridView列的代码,因为当您绑定DataGridView时,它会根据DataTable构造其列。


3
其中最简单的方法是使用BindingListView类来包装您的DisplayJobs列表。该类实现了一些必需的接口,使得在DataGridView中进行排序和过滤变得容易。这是一个快速的方式。它工作得非常好,唯一需要注意的是,如果您从DataGridView中获取数据,您需要将其转换为包装对象(ObjectView)而不是实际的项(DisplayJob)。
另外一种较少懒惰的方法是创建一个自定义的集合类型,实现IBindingList接口,并在其中实现排序方法。

BindingListView可以通过Nuget方便地获取:Install-Package Unofficial.BindingListView - user1016736

3
Daok提供的微软文章让我找到了正确的方向,但我对微软的SortableSearchableList实现并不满意。我发现这个实现非常奇怪,当列中存在重复值时,它无法很好地工作。它还没有覆盖IsSortedCore,而DataGridView似乎需要这样做。如果未覆盖IsSortedCore,则搜索图标不会出现,并且在升序和降序之间切换也无法正常工作。
请查看下面我修改后的SortableSearchableList版本。在ApplySortCore()中,它使用设置为匿名方法的Comparison委托进行排序。此版本还支持为特定属性设置自定义比较,可以由派生类使用AddCustomCompare()进行添加。
我不确定版权声明是否仍然适用,但我只是将其保留了下来。
//---------------------------------------------------------------------
//  Copyright (C) Microsoft Corporation.  All rights reserved.
// 
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Collections;

namespace SomethingSomething
{
    /// <summary>
    /// Supports sorting of list in data grid view.
    /// </summary>
    /// <typeparam name="T">Type of object to be displayed in data grid view.</typeparam>
    public class SortableSearchableList<T> : BindingList<T>
    {
        #region Data Members

        private ListSortDirection _sortDirectionValue;
        private PropertyDescriptor _sortPropertyValue = null;

        /// <summary>
        /// Dictionary from property name to custom comparison function.
        /// </summary>
        private Dictionary<string, Comparison<T>> _customComparisons = new Dictionary<string, Comparison<T>>();

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public SortableSearchableList()
        {
        }

        #endregion

        #region Properties

        /// <summary>
        /// Indicates if sorting is supported.
        /// </summary>
        protected override bool SupportsSortingCore
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Indicates if list is sorted.
        /// </summary>
        protected override bool IsSortedCore
        {
            get
            {
                return _sortPropertyValue != null;
            }
        }

        /// <summary>
        /// Indicates which property the list is sorted.
        /// </summary>
        protected override PropertyDescriptor SortPropertyCore
        {
            get
            {
                return _sortPropertyValue;
            }
        }

        /// <summary>
        /// Indicates in which direction the list is sorted on.
        /// </summary>
        protected override ListSortDirection SortDirectionCore
        {
            get
            {
                return _sortDirectionValue;
            }
        }

        #endregion

        #region Methods       

        /// <summary>
        /// Add custom compare method for property.
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="compareProperty"></param>
        protected void AddCustomCompare(string propertyName, Comparison<T> comparison)
        {
            _customComparisons.Add(propertyName, comparison);
        }

        /// <summary>
        /// Apply sort.
        /// </summary>
        /// <param name="prop"></param>
        /// <param name="direction"></param>
        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            Comparison<T> comparison;
            if (!_customComparisons.TryGetValue(prop.Name, out comparison))
            {
                // Check to see if the property type we are sorting by implements
                // the IComparable interface.
                Type interfaceType = prop.PropertyType.GetInterface("IComparable");
                if (interfaceType != null)
                {
                    comparison = delegate(T t1, T t2)
                        {
                            IComparable val1 = (IComparable)prop.GetValue(t1);
                            IComparable val2 = (IComparable)prop.GetValue(t2);
                            return val1.CompareTo(val2);
                        };
                }
                else
                {
                    // Last option: convert to string and compare.
                    comparison = delegate(T t1, T t2)
                        {
                            string val1 = prop.GetValue(t1).ToString();
                            string val2 = prop.GetValue(t2).ToString();
                            return val1.CompareTo(val2);
                        };
                }
            }

            if (comparison != null)
            {
                // If so, set the SortPropertyValue and SortDirectionValue.
                _sortPropertyValue = prop;
                _sortDirectionValue = direction;

                // Create sorted list.
                List<T> _sortedList = new List<T>(this);                   
                _sortedList.Sort(comparison);

                // Reverse order if needed.
                if (direction == ListSortDirection.Descending)
                {
                    _sortedList.Reverse();
                }

                // Update list.
                int count = this.Count;
                for (int i = 0; i < count; i++)
                {
                    this[i] = _sortedList[i];
                }

                // Raise the ListChanged event so bound controls refresh their
                // values.
                OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
            }
        }

        // Method below was in the original implementation from MS. Don't know what it's for.
        // -- Martijn Boeker, Jan 21, 2010

        //protected override void RemoveSortCore()
        //{
        //    //int position;
        //    //object temp;
        //    //// Ensure the list has been sorted.
        //    //if (unsortedItems != null)
        //    //{
        //    //    // Loop through the unsorted items and reorder the
        //    //    // list per the unsorted list.
        //    //    for (int i = 0; i < unsortedItems.Count; )
        //    //    {
        //    //        position = this.Find(SortPropertyCore.Name,
        //    //            unsortedItems[i].GetType().
        //    //            GetProperty(SortPropertyCore.Name).
        //    //            GetValue(unsortedItems[i], null));
        //    //        if (position >= 0 && position != i)
        //    //        {
        //    //            temp = this[i];
        //    //            this[i] = this[position];
        //    //            this[position] = (T)temp;
        //    //            i++;
        //    //        }
        //    //        else if (position == i)
        //    //            i++;
        //    //        else
        //    //            // If an item in the unsorted list no longer exists, delete it.
        //    //            unsortedItems.RemoveAt(i);
        //    //    }
        //    //    OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        //    //}
        //}

        /// <summary>
        /// Ability to search an item.
        /// </summary>
        protected override bool SupportsSearchingCore
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Finds an item in the list.
        /// </summary>
        /// <param name="prop"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        protected override int FindCore(PropertyDescriptor prop, object key)
        {
            // Implementation not changed from MS example code.

            // Get the property info for the specified property.
            PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);
            T item;

            if (key != null)
            {
                // Loop through the 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))
                        return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// Finds an item in the list.
        /// </summary>
        /// <param name="prop"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private int Find(string property, object key)
        {
            // Implementation not changed from MS example code.

            // 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)
                return -1;
            else
                return FindCore(prop, key);
        }

        #endregion
    }
}

终于有人加入了命名空间,给他们点赞。 - SteveCav

1

我相信你的类必须实现 IComparable 接口。

希望能有所帮助,

Bruno Figueiredo


1
我建议替换为:
jobs = new List<DisplayJob>();

使用:

jobs = new SortableBindingList<DisplayJob>();

SortableBindingList的代码在这里:http://www.timvw.be/presenting-the-sortablebindinglistt/

我已经在生产环境中使用了基于此代码的内容,没有遇到任何问题。它唯一的限制是它不是一个稳定的排序。

如果你想要排序是稳定的,请替换:

itemsList.Sort(delegate(T t1, T t2)
{
    object value1 = prop.GetValue(t1);
    object value2 = prop.GetValue(t2);

    return reverse * Comparer.Default.Compare(value1, value2);
});

使用插入排序:

int j;
T index;
for (int i = 0; i < itemsList.Count; i++)
{
    index = itemsList[i];
    j = i;

    while ((j > 0) && (reverse * Comparer.Default.Compare(prop.GetValue(itemsList[j - 1]), prop.GetValue(index)) > 0))
    {
        itemsList[j] = itemsList[j - 1];
        j = j - 1;
    }

    itemsList[j] = index;
}

0

你尝试为每列设置SortMemberPath了吗?

uxJobList.Columns[newColumn].SortMemberPath="Id";

我现在使用ObservableCollection,而不是List。


0
Martijn的代码很棒,但是有一个细节需要注意,你需要验证空单元格或空值 :)
if (!_customComparisons.TryGetValue(prop.Name, out comparison))
{
    // Check to see if the property type we are sorting by implements
    // the IComparable interface.
    Type interfaceType = prop.PropertyType.GetInterface("IComparable");
    if (interfaceType != null)
    {
        comparison = delegate(T t1, T t2)
            {
                IComparable val1 = (IComparable)prop.GetValue(t1) ?? "";
                IComparable val2 = (IComparable)prop.GetValue(t2) ?? "";
                return val1.CompareTo(val2);
            };
    }
    else
    {
        // Last option: convert to string and compare.
        comparison = delegate(T t1, T t2)
            {
                string val1 = (prop.GetValue(t1) ?? "").ToString();
                string val2 = (prop.GetValue(t2) ?? "").ToString();
                return val1.CompareTo(val2);
            };
    }
}

那就全靠运气了


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接