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

ListView 列排序器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.36/5 (39投票s)

2003年10月30日

CPOL

1分钟阅读

viewsIcon

327544

downloadIcon

6665

一个列排序器,可以对包含字符串和数字(包括小数)的列进行排序,还可以对第一列按照图像然后按照字符串进行排序。

引言

很久以前我就开始使用 ListView。过了一段时间,我急需一个列排序器。我只想对每一列进行排序。后来,我想也许也可以对图像进行排序。当然,在这种情况下,它应该先按图像排序,然后再按第一列中的文本排序。昨天我遇到了一个问题,一列包含字符串形式的数字。这又一次让我进入下一个版本,该版本应该确定字符串是否实际上是一个数字,然后按数字进行排序。我已经创建了一个示例项目,该项目为您利用了所有这些功能。请查看一下。

如何使用

如果您只想使用它,这非常简单。只需从页面顶部的链接下载源代码压缩包。在您的表单类中,只需声明

private ListViewColumnSorter lvwColumnSorter;

然后在表单的构造函数中,简单地添加

private void MyForm()
{
  lvwColumnSorter = new ListViewColumnSorter();
  this.listView1.ListViewItemSorter = lvwColumnSorter;
}

当然,现在您希望能够在单击列标题时进行排序。现在在 Visual Studio 中,选择您的 ListView 并转到属性,选择事件,然后双击ColumnClick。这将创建以下内容

this.listView1.ColumnClick += 
 new System.Windows.Forms.ColumnClickEventHandler(this.listView1_ColumnClick);

这意味着,我们需要另一个方法来为我们控制排序

private void listView1_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
{
   ListView myListView = (ListView)sender;

   // Determine if clicked column is already the column that is being sorted.
   if ( e.Column == lvwColumnSorter.SortColumn )
   {
     // Reverse the current sort direction for this column.
     if (lvwColumnSorter.Order == SortOrder.Ascending)
     {
      lvwColumnSorter.Order = SortOrder.Descending;
     }
     else
     {
      lvwColumnSorter.Order = SortOrder.Ascending;
     }
   }
   else
   {
    // Set the column number that is to be sorted; default to ascending.
    lvwColumnSorter.SortColumn = e.Column;
    lvwColumnSorter.Order = SortOrder.Ascending;
   }

   // Perform the sort with these new sort options.
   myListView.Sort();
}

现在您完成了...

代码基础

using System;
using System.Collections;
using System.Text.RegularExpressions;    
using System.Windows.Forms;
using System.Globalization;

namespace ListViewSorter
{
    /// <summary>
    /// This class is an implementation of the 'IComparer' interface.
    /// </summary>
    public class ListViewColumnSorter : IComparer
    {
        public enum SortModifiers
        {
            SortByImage,
            SortByCheckbox,
            SortByText
        }

        /// <summary>
        /// Specifies the column to be sorted
        /// </summary>
        public int ColumnToSort;
        /// <summary>
        /// Specifies the order in which to sort (i.e. 'Ascending').
        /// </summary>
        public SortOrder OrderOfSort;
        /// <summary>
        /// Case insensitive comparer object
        /// </summary>
        
        private NumberCaseInsensitiveComparer ObjectCompare;
        private ImageTextComparer FirstObjectCompare;
        private CheckboxTextComparer FirstObjectCompare2;

        private SortModifiers mySortModifier = SortModifiers.SortByText;
        public SortModifiers _SortModifier
        {
            set
            {
                mySortModifier = value;
            }
            get
            {
                return mySortModifier;
            }
        }

        /// <summary>
        /// Class constructor.  Initializes various elements
        /// </summary>
        public ListViewColumnSorter()
        {
            // Initialize the column to '0'
            ColumnToSort = 0;

            // Initialize the CaseInsensitiveComparer object
            ObjectCompare = new NumberCaseInsensitiveComparer();
            FirstObjectCompare = new ImageTextComparer();
            FirstObjectCompare2 = new CheckboxTextComparer();
        }

        /// <summary>
        /// This method is inherited from the IComparer interface.  It compares the two objects passed using a case insensitive comparison.
        /// </summary>
        /// <param name="x">First object to be compared</param>
        /// <param name="y">Second object to be compared</param>
        /// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
        public int Compare(object x, object y)
        {
            int compareResult = 0;
            ListViewItem listviewX, listviewY;

            // Cast the objects to be compared to ListViewItem objects
            listviewX = (ListViewItem)x;
            listviewY = (ListViewItem)y;

            ListView listViewMain = listviewX.ListView;

            // Calculate correct return value based on object comparison
            if (listViewMain.Sorting != SortOrder.Ascending &&
                listViewMain.Sorting != SortOrder.Descending)
            {
                // Return '0' to indicate they are equal
                return compareResult;
            }

            if (mySortModifier.Equals(SortModifiers.SortByText) || ColumnToSort > 0)
            {
                // Compare the two items

                if (listviewX.SubItems.Count <= ColumnToSort &&
                    listviewY.SubItems.Count <= ColumnToSort)
                {
                    compareResult = ObjectCompare.Compare(null, null);
                }
                else if (listviewX.SubItems.Count <= ColumnToSort &&
                         listviewY.SubItems.Count > ColumnToSort)
                {
                    compareResult = ObjectCompare.Compare(null, listviewY.SubItems[ColumnToSort].Text.Trim());
                }
                else if (listviewX.SubItems.Count > ColumnToSort && listviewY.SubItems.Count <= ColumnToSort)
                {
                    compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text.Trim(), null);
                }
                else
                {
                    compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text.Trim(), listviewY.SubItems[ColumnToSort].Text.Trim());
                }
            }
            else
            {
                switch (mySortModifier)
                {
                    case SortModifiers.SortByCheckbox:
                        compareResult = FirstObjectCompare2.Compare(x, y);
                        break;
                    case SortModifiers.SortByImage:
                        compareResult = FirstObjectCompare.Compare(x, y);
                        break;
                    default:
                        compareResult = FirstObjectCompare.Compare(x, y);
                        break;
                }
            }

            // Calculate correct return value based on object comparison
            if (OrderOfSort == SortOrder.Ascending)
            {
                // Ascending sort is selected, return normal result of compare operation
                return compareResult;
            }
            else if (OrderOfSort == SortOrder.Descending)
            {
                // Descending sort is selected, return negative result of compare operation
                return (-compareResult);
            }
            else
            {
                // Return '0' to indicate they are equal
                return 0;
            }
        }
    
        /// <summary>
        /// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
        /// </summary>
        public int SortColumn
        {
            set
            {
                ColumnToSort = value;
            }
            get
            {
                return ColumnToSort;
            }
        }

        /// <summary>
        /// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
        /// </summary>
        public SortOrder Order
        {
            set
            {
                OrderOfSort = value;
            }
            get
            {
                return OrderOfSort;
            }
        }
    
    }

    public class ImageTextComparer : IComparer
    {
        //private CaseInsensitiveComparer ObjectCompare;
        private NumberCaseInsensitiveComparer ObjectCompare;
        
        public ImageTextComparer()
        {
            // Initialize the CaseInsensitiveComparer object
            ObjectCompare = new NumberCaseInsensitiveComparer();
        }

        public int Compare(object x, object y)
        {
            //int compareResult;
            int image1, image2;
            ListViewItem listviewX, listviewY;

            // Cast the objects to be compared to ListViewItem objects
            listviewX = (ListViewItem)x;
            image1 = listviewX.ImageIndex;
            listviewY = (ListViewItem)y;
            image2 = listviewY.ImageIndex;

            if (image1 < image2)
            {
                return -1;
            }
            else if (image1 == image2)
            {
                return ObjectCompare.Compare(listviewX.Text.Trim(), listviewY.Text.Trim());
            }
            else
            {
                return 1;
            }
        }
    }

    public class CheckboxTextComparer : IComparer
    {
        private NumberCaseInsensitiveComparer ObjectCompare;

        public CheckboxTextComparer()
        {
            // Initialize the CaseInsensitiveComparer object
            ObjectCompare = new NumberCaseInsensitiveComparer();
        }

        public int Compare(object x, object y)
        {
            // Cast the objects to be compared to ListViewItem objects
            ListViewItem listviewX = (ListViewItem)x;
            ListViewItem listviewY = (ListViewItem)y;

            if (listviewX.Checked && !listviewY.Checked)
            {
                return -1;
            }
            else if (listviewX.Checked.Equals(listviewY.Checked))
            {
                if (listviewX.ImageIndex < listviewY.ImageIndex)
                {
                    return -1;
                }
                else if (listviewX.ImageIndex == listviewY.ImageIndex)
                {
                    return ObjectCompare.Compare(listviewX.Text.Trim(), listviewY.Text.Trim());
                }
                else
                {
                    return 1;
                }
            }
            else
            {
                return 1;
            }
        }
    }

    public class NumberCaseInsensitiveComparer : CaseInsensitiveComparer
    {
        public NumberCaseInsensitiveComparer ()
        {
            
        }
        
        public new int Compare(object x, object y)
        {
            if (x == null && y == null)
            {
                return 0;
            }
            else if (x == null && y != null)
            {
                return -1;
            }
            else if (x != null && y == null)
            {
                return 1;
            }

            if ((x is System.String) && IsDecimalNumber((string)x) && (y is System.String) && IsDecimalNumber((string)y))
            {
                try
                {
                    decimal xx= Decimal.Parse(((string)x).Trim());
                    decimal yy= Decimal.Parse(((string)y).Trim());

                    return base.Compare(xx,yy);
                }
                catch
                {
                    return -1;
                }
            }
            else
            {
                return base.Compare(x,y);
            }
        }

        // deprecated
        //private bool IsWholeNumber(string strNumber)
        //{
        //    Regex wholePattern = new Regex(@"^\d+$");
        //    return wholePattern.IsMatch(strNumber);
        //}

        private string GetNumberDecimalSeparator()
        {
            return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
        }

        // http://stackoverflow.com/questions/4246077/matching-numbers-with-regular-expressions-only-digits-and-commas/4247184#4247184
        // https://www.debuggex.com/r/Lyx0F0y1LORvNhwA
        private bool IsDecimalNumber(string strNumber)
        {
            //@"^-?(\d+|(\d{1,3}((,|\.)\d{3})*))((,|\.)\d+)?$"

            //string regex = @"^-?(?:(?:0|[1-9][0-9]*)(?:" + GetNumberDecimalSeparator() + @"[0-9]+)?|[1-9][0-9]{1,2}(?:,[0-9]{3})+)$";

            string regex = @"^-?(\d+|(\d{1,3}((,|\.)\d{3})*))((,|\.)\d+)?$";

            Regex wholePattern = new Regex(regex);
            return wholePattern.IsMatch(strNumber);
        }
    }

}

历史

2003-11-04 - 已将“如何使用它”添加到描述中。

2013-08-16 - 添加了一些错误修复(数字排序、int64 排序、空子项)并添加了复选框排序

2015-07-21 - 添加了小数排序,修复了“SortOrder”与其他类冲突的可能命名空间错误

© . All rights reserved.