【问题标题】:DataGridView Column sorting with Business Objects使用业务对象对 DataGridView 列进行排序
【发布时间】:2008-11-11 13:58:55
【问题描述】:

我正在像这样设置我的 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; }
}

但是列排序不起作用。实现此功能的最佳方法是什么?

【问题讨论】:

    标签: c# .net datagridview


    【解决方案1】:

    如果您想支持对集合进行排序和搜索,所有都需要从您的 BindingList 参数化类型派生一个类,并覆盖一些基类方法和属性。

    最好的方法是扩展 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");
    }
    

    如果您需要更多信息,您可以随时前往那里了解有关how to extend the binding list 的所有说明。

    【讨论】:

      【解决方案2】:

      Daok 的解决方案是正确的。它也经常比它的价值更多的工作。

      懒人获得所需功能的方法是从业务对象中创建和填充 DataTable,并将 DataGridView 绑定到它。

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

      但它很容易编写,而且生成的代码比IBindingList 的实现更神秘。

      此外,无论如何,您已经编写了很多代码,或者至少是类似的代码:您编写用于定义 DataTable 的代码使您不必编写代码来创建 DataGridView 的列,因为 DataGridView 将绑定时从 DataTable 构造它的列。

      【讨论】:

        【解决方案3】:

        最简单的方法之一是使用BindingListView 类来包装您的DisplayJobs 列表。该类实现了一些在 DataGridView 中启用排序和筛选所需的接口。那是快速的方法。不过,它工作得很好——唯一需要注意的是,如果你从 DataGridView 中投射东西,你需要投射到包装对象 (ObjectView) 而不是实际的项目 (DisplayJob)。

        比较懒的方法是创建一个自定义的集合时间,实现IBindingList,在那里实现排序方法。

        【讨论】:

        • BindingListView 可以通过 Nuget 方便地获得:Install-Package Unofficial.BindingListView
        【解决方案4】:

        Daok 建议的 MS 文章让我走上了正轨,但我对 MS 的 SortableSearchableList 实现并不满意。我发现这个实现很奇怪,当列中有重复值时它不能很好地工作。它也不会覆盖 DataGridView 似乎需要的 IsSortedCore。如果未覆盖 IsSortedCore,则不会出现搜索字形,并且无法在升序和降序之间切换。

        在下面查看我的 SortableSearchableList 版本。在 ApplySortCore() 中,它使用设置为匿名方法的比较委托进行排序。此版本还支持为特定属性设置自定义比较,可以通过派生类使用 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
            }
        }
        

        【讨论】:

        • +1 表示有人最终放入了他们包含的命名空间。
        【解决方案5】:

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

        希望对你有帮助,

        布鲁诺·菲格雷多

        【讨论】:

          【解决方案6】:

          我建议更换:

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

          【讨论】:

            【解决方案7】:

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

            这都是运气

            【讨论】:

              【解决方案8】:

              您是否尝试为每一列设置 SortMemberPath?

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

              而不是 List im 只是使用 ObservableCollection

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-12-05
                • 2011-06-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多