ListView按列排序

24

目前我在listview上使用自定义排序器,每次点击“第一列”时都可以对listview进行排序,但是它无法按其他列排序。

SortStyle:用于确定升序排序还是降序排序的变量。

if (e.Column == 0)
{
    if (SortStyle == 0)
    {
        List.ListViewItemSorter = customSortDsc;
        SortStyle = 1;
    }
    else
    {
        List.ListViewItemSorter = customSortAsc;
        SortStyle = 0;
    }
}

当第一列排序时,这很好用,但如果你在任何其他列上进行排序,它只会按第一列排序。有没有办法按点击的列排序?


Mike,如果您看一下我下面的示例,我认为您正在寻找ColumnClickEventArgs.Column值。它会告诉您单击了哪个列标题。 - Wil P
15个回答

31

放弃你的自定义排序器,从以下页面使用代码重新开始。它将向你展示如何定义一个从IComparer接口继承的类。每行都有注释,因此你可以看到实际发生了什么。唯一可能复杂的是如何从ListView控件中检索ListView项。解决这些问题后,你只需要复制并粘贴IComparer接口类和columnClick方法即可。

http://support.microsoft.com/kb/319401


2
非常有用的链接。看起来是最简单的解决方案。谢谢RedEye。 - Martin
2
或者更好的是,使用上面链接的代码创建自己的SortableListView,然后您只需要更改一个初始化列表的地方即可。 - Ryan Anderson
2
由于事件接收器中的代码是标准行为,我在ListViewColumnSorter类中添加了一个ReverseSortOrderAndSort(int column, ListView lv)方法,以便事件接收器中的代码变为:private void listView1_ColumnClick(object sender, ColumnClickEventArgs e) { listViewColumnSorter.ReverseSortOrderAndSort(e.Column, (ListView)sender); } - Jack Griffin
1
一种漂亮无痛的解决方案,完全跳过GNU头疼问题,ObjectListView附带。 - The Lemon

20

如果你初次使用ListView,强烈建议使用ObjectListView,它是一个开源封装了.NET WinForms ListView的工具,可以让ListView更易于使用,并自动解决许多常见问题。其中之一是列点击排序。

真的,你不会后悔使用ObjectListView代替普通ListView。


13
我之前用过 ObjectListView,觉得它不错。不过你需要知道的一件事是它采用 GPL 开源协议,因此你可能需要公开你的源代码。 - Slapout
可能需要?有人知道我们是否可以使用GPL吗?我真正想知道的是:我是否可以在商业软件中合法使用它,而该软件是销售的而不是开源的?(在欧洲和美国) - noelicus
如果您想在闭源应用程序中使用此内容,可以购买(便宜的)许可证:http://objectlistview.sourceforge.net/cs/faq.html#can-i-use-objectlistview-in-a-commercial-application - Grammarian
有些东西在 x64 下是有问题的(例如分组)。 - Sebastian
ObjectListView在x64下运行良好(这是我的日常工作环境),包括分组功能。如果您遇到问题,为什么不在讨论组https://sourceforge.net/p/objectlistview/discussion/812922上发布消息呢? - Grammarian

6
我使用列名进行排序,以设置可能需要基于列中存储的数据类型或该列是否已按升序/降序排序处理的任何排序特定信息。以下是我ColumnClick事件处理程序的一部分片段。
private void listView_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        ListViewItemComparer sorter = GetListViewSorter(e.Column);

        listView.ListViewItemSorter = sorter;
        listView.Sort();
    }

    private ListViewItemComparer GetListViewSorter(int columnIndex)
    {
        ListViewItemComparer sorter = (ListViewItemComparer)listView.ListViewItemSorter;
        if (sorter == null)
        {
            sorter = new ListViewItemComparer();
        }

        sorter.ColumnIndex = columnIndex;

        string columnName = packagedEstimateListView.Columns[columnIndex].Name;
        switch (columnName)
        {
            case ApplicationModel.DisplayColumns.DateCreated:
            case ApplicationModel.DisplayColumns.DateUpdated:
                sorter.ColumnType = ColumnDataType.DateTime;
                break;
            case ApplicationModel.DisplayColumns.NetTotal:
            case ApplicationModel.DisplayColumns.GrossTotal:
                sorter.ColumnType = ColumnDataType.Decimal;
                break;
            default:
                sorter.ColumnType = ColumnDataType.String;
                break;
        }

        if (sorter.SortDirection == SortOrder.Ascending)
        {
            sorter.SortDirection = SortOrder.Descending;
        }
        else
        {
            sorter.SortDirection = SortOrder.Ascending;
        }

        return sorter;
    }

以下是我的ListViewItemComparer。
public class ListViewItemComparer : IComparer
{
    private int _columnIndex;
    public int ColumnIndex
    {
        get
        {
            return _columnIndex;
        }
        set
        {
            _columnIndex = value;
        }
    }

    private SortOrder _sortDirection;
    public SortOrder SortDirection
    {
        get
        {
            return _sortDirection;
        }
        set
        {
            _sortDirection = value;
        }
    }

    private ColumnDataType _columnType;
    public ColumnDataType ColumnType
    {
        get
        {
            return _columnType;
        }
        set
        {
            _columnType = value;
        }
    }


    public ListViewItemComparer()
    {
        _sortDirection = SortOrder.None;
    }

    public int Compare(object x, object y)
    {
        ListViewItem lviX = x as ListViewItem;
        ListViewItem lviY = y as ListViewItem;

        int result;

        if (lviX == null && lviY == null)
        {
            result = 0;
        }
        else if (lviX == null)
        {
            result = -1;
        }

        else if (lviY == null)
        {
            result = 1;
        }

        switch (ColumnType)
        {
            case ColumnDataType.DateTime:
                DateTime xDt = DataParseUtility.ParseDate(lviX.SubItems[ColumnIndex].Text);
                DateTime yDt = DataParseUtility.ParseDate(lviY.SubItems[ColumnIndex].Text);
                result = DateTime.Compare(xDt, yDt);
                break;

            case ColumnDataType.Decimal:
                Decimal xD = DataParseUtility.ParseDecimal(lviX.SubItems[ColumnIndex].Text.Replace("$", string.Empty).Replace(",", string.Empty));
                Decimal yD = DataParseUtility.ParseDecimal(lviY.SubItems[ColumnIndex].Text.Replace("$", string.Empty).Replace(",", string.Empty));
                result = Decimal.Compare(xD, yD);
                break;
            case ColumnDataType.Short:
                short xShort = DataParseUtility.ParseShort(lviX.SubItems[ColumnIndex].Text);
                short yShort = DataParseUtility.ParseShort(lviY.SubItems[ColumnIndex].Text);
                result = xShort.CompareTo(yShort);
                break;
            case ColumnDataType.Int:
                int xInt = DataParseUtility.ParseInt(lviX.SubItems[ColumnIndex].Text);
                int yInt = DataParseUtility.ParseInt(lviY.SubItems[ColumnIndex].Text);
                return xInt.CompareTo(yInt);
                break;
            case ColumnDataType.Long:
                long xLong = DataParseUtility.ParseLong(lviX.SubItems[ColumnIndex].Text);
                long yLong = DataParseUtility.ParseLong(lviY.SubItems[ColumnIndex].Text);
                return xLong.CompareTo(yLong);
                break;
            default:

                result = string.Compare(
                    lviX.SubItems[ColumnIndex].Text,
                    lviY.SubItems[ColumnIndex].Text,
                    false);

                break;
        }

        if (SortDirection == SortOrder.Descending)
        {
            return -result;
        }
        else
        {
            return result;
        }
    }
}

这个页面提供了一个类似的解决方案 -> http://www.java2s.com/Tutorial/CSharp/0460__GUI-Windows-Forms/ListViewSorter.htm - John M
当根据我提供的链接创建基于ListViewitemComparer时,您需要添加以下代码以获取升序/降序排序: string itemXText = itemX.SubItems[Column].Text; string itemYText = itemY.SubItems[Column].Text;//排序 if (itemX.ListView.Sorting == SortOrder.Ascending) { return String.Compare(itemXText, itemYText); } else { return String.Compare(itemYText, itemXText); } - John M
@will-p,你能展示一下ListViewItemComparer是什么样子的吗? - slayernoah
@slayernoah - 请看我上面更新的ListItemViewComparer。 - Wil P
你在哪里(以及如何)定义自己的类型 ColumnDataType - PeterCo

4
我用了这个技巧。
private void lv_TavComEmpty_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            ListView lv = (ListView)sender;

            //propriety SortOrder make me some problem on graphic layout
            //i use this tag to set last order
            if (lv.Tag == null || (int)lv.Tag > 0)
            //if (lv.Sorting == SortOrder.Ascending)
            {
                ListViewItem[] tmp = lv.Items.Cast<ListViewItem>().OrderBy(t => t.SubItems[e.Column].Text).ToArray();
                lv.Items.Clear();
                lv.Items.AddRange(tmp);

                lv.Tag = -1;
                //lv.Sorting = SortOrder.Descending;
            }
            else
            {
                ListViewItem[] tmp = lv.Items.Cast<ListViewItem>().OrderByDescending(t => t.SubItems[e.Column].Text).ToArray();
                lv.Items.Clear();
                lv.Items.AddRange(tmp);

                lv.Tag = +1;
                //lv.Sorting = SortOrder.Ascending;
            }
        }

最佳答案-简短-紧凑。没有经过广泛测试,但在详细视图上似乎很好地完成了基本任务。 - stigzler

4

对于ListView中字符串和数字值的排序,我对这篇文章进行了一些微小的修改。

Form1.cs 包含以下内容:

using System;
using System.Windows.Forms;

namespace ListView
{
    public partial class Form1 : Form
    {
        Random rnd = new Random();
        private ListViewColumnSorter lvwColumnSorter;

        public Form1()
        {
            InitializeComponent();
            // Create an instance of a ListView column sorter and assign it to the ListView control.
            lvwColumnSorter = new ListViewColumnSorter();
            this.listView1.ListViewItemSorter = lvwColumnSorter;

            InitListView();
        }

        private void InitListView()
        {
            listView1.View = View.Details;
            listView1.GridLines = true;
            listView1.FullRowSelect = true;

            //Add column header
            listView1.Columns.Add("Name", 100);
            listView1.Columns.Add("Price", 70);
            listView1.Columns.Add("Trend", 70);

            for (int i = 0; i < 10; i++)
            {
                listView1.Items.Add(AddToList("Name" + i.ToString(), rnd.Next(1, 100).ToString(), rnd.Next(1, 100).ToString()));
            }
        }

        private ListViewItem AddToList(string name, string price, string trend)
        {
            string[] array = new string[3];
            array[0] = name;
            array[1] = price;
            array[2] = trend;

            return (new ListViewItem(array));
        }

        private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // 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.
            this.listView1.Sort();
        }

    }
}

ListViewColumnSorter.cs 包含以下内容:
using System;
using System.Collections;
using System.Windows.Forms;

/// <summary>
/// This class is an implementation of the 'IComparer' interface.
/// </summary>
public class ListViewColumnSorter : IComparer
{
    /// <summary>
    /// Specifies the column to be sorted
    /// </summary>
    private int ColumnToSort;
    /// <summary>
    /// Specifies the order in which to sort (i.e. 'Ascending').
    /// </summary>
    private SortOrder OrderOfSort;
    /// <summary>
    /// Case insensitive comparer object
    /// </summary>
    private CaseInsensitiveComparer ObjectCompare;

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

        // Initialize the sort order to 'none'
        OrderOfSort = SortOrder.None;

        // Initialize the CaseInsensitiveComparer object
        ObjectCompare = new CaseInsensitiveComparer();
    }

    /// <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;
        ListViewItem listviewX, listviewY;

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

        decimal num = 0;
        if (decimal.TryParse(listviewX.SubItems[ColumnToSort].Text, out num))
        {
            compareResult = decimal.Compare(num, Convert.ToDecimal(listviewY.SubItems[ColumnToSort].Text));
        }
        else 
        {
            // Compare the two items
            compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
        }

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

}

2

我的解决方案是一个类,用于在单击列标题时对listView项目进行排序。

您可以指定每列的类型。

listView.ListViewItemSorter = new ListViewColumnSorter();
listView.ListViewItemSorter.ColumnsTypeComparer.Add(0, DateTime);
listView.ListViewItemSorter.ColumnsTypeComparer.Add(1, int);

这就是了!
C#类:
using System.Collections;
using System.Collections.Generic;
using EDV;

namespace System.Windows.Forms
{
    /// <summary>
    /// Cette classe est une implémentation de l'interface 'IComparer' pour le tri des items de ListView. Adapté de http://support.microsoft.com/kb/319401.
    /// </summary>
    /// <remarks>Intégré par EDVariables.</remarks>
    public class ListViewColumnSorter : IComparer
    {
        /// <summary>
        /// Spécifie la colonne à trier
        /// </summary>
        private int ColumnToSort;
        /// <summary>
        /// Spécifie l'ordre de tri (en d'autres termes 'Croissant').
        /// </summary>
        private SortOrder OrderOfSort;
        /// <summary>
        /// Objet de comparaison ne respectant pas les majuscules et minuscules
        /// </summary>
        private CaseInsensitiveComparer ObjectCompare;

        /// <summary>
        /// Constructeur de classe.  Initialise la colonne sur '0' et aucun tri
        /// </summary>
        public ListViewColumnSorter()
            : this(0, SortOrder.None) { }

        /// <summary>
        /// Constructeur de classe.  Initializes various elements
        /// <param name="columnToSort">Spécifie la colonne à trier</param>
        /// <param name="orderOfSort">Spécifie l'ordre de tri</param>
        /// </summary>
        public ListViewColumnSorter(int columnToSort, SortOrder orderOfSort)
        {
            // Initialise la colonne
            ColumnToSort = columnToSort;

            // Initialise l'ordre de tri
            OrderOfSort = orderOfSort;

            // Initialise l'objet CaseInsensitiveComparer
            ObjectCompare = new CaseInsensitiveComparer();

            // Dictionnaire de comparateurs
            ColumnsComparer = new Dictionary<int, IComparer>();
            ColumnsTypeComparer = new Dictionary<int, Type>();

        }

        /// <summary>
        /// Cette méthode est héritée de l'interface IComparer.  Il compare les deux objets passés en effectuant une comparaison 
        ///qui ne tient pas compte des majuscules et des minuscules.
        /// <br/>Si le comparateur n'existe pas dans ColumnsComparer, CaseInsensitiveComparer est utilisé.
        /// </summary>
        /// <param name="x">Premier objet à comparer</param>
        /// <param name="x">Deuxième objet à comparer</param>
        /// <returns>Le résultat de la comparaison. "0" si équivalent, négatif si 'x' est inférieur à 'y' 
        ///et positif si 'x' est supérieur à 'y'</returns>
        public int Compare(object x, object y)
        {
            int compareResult;
            ListViewItem listviewX, listviewY;

            // Envoit les objets à comparer aux objets ListViewItem
            listviewX = (ListViewItem)x;
            listviewY = (ListViewItem)y;

            if (listviewX.SubItems.Count < ColumnToSort + 1 || listviewY.SubItems.Count < ColumnToSort + 1)
                return 0;

            IComparer objectComparer = null;
            Type comparableType = null;
            if (ColumnsComparer == null || !ColumnsComparer.TryGetValue(ColumnToSort, out objectComparer))
                if (ColumnsTypeComparer == null || !ColumnsTypeComparer.TryGetValue(ColumnToSort, out comparableType))
                    objectComparer = ObjectCompare;

            // Compare les deux éléments
            if (comparableType != null) {
                //Conversion du type
                object valueX = listviewX.SubItems[ColumnToSort].Text;
                object valueY = listviewY.SubItems[ColumnToSort].Text;
                if (!edvTools.TryParse(ref valueX, comparableType) || !edvTools.TryParse(ref valueY, comparableType))
                    return 0;
                compareResult = (valueX as IComparable).CompareTo(valueY);
            }
            else
                compareResult = objectComparer.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);

            // Calcule la valeur correcte d'après la comparaison d'objets
            if (OrderOfSort == SortOrder.Ascending) {
                // Le tri croissant est sélectionné, renvoie des résultats normaux de comparaison
                return compareResult;
            }
            else if (OrderOfSort == SortOrder.Descending) {
                // Le tri décroissant est sélectionné, renvoie des résultats négatifs de comparaison
                return (-compareResult);
            }
            else {
                // Renvoie '0' pour indiquer qu'ils sont égaux
                return 0;
            }
        }

        /// <summary>
        /// Obtient ou définit le numéro de la colonne à laquelle appliquer l'opération de tri (par défaut sur '0').
        /// </summary>
        public int SortColumn
        {
            set
            {
                ColumnToSort = value;
            }
            get
            {
                return ColumnToSort;
            }
        }

        /// <summary>
        /// Obtient ou définit l'ordre de tri à appliquer (par exemple, 'croissant' ou 'décroissant').
        /// </summary>
        public SortOrder Order
        {
            set
            {
                OrderOfSort = value;
            }
            get
            {
                return OrderOfSort;
            }
        }

        /// <summary>
        /// Dictionnaire de comparateurs par colonne.
        /// <br/>Pendant le tri, si le comparateur n'existe pas dans ColumnsComparer, CaseInsensitiveComparer est utilisé.
        /// </summary>
        public Dictionary<int, IComparer> ColumnsComparer { get; set; }

        /// <summary>
        /// Dictionnaire de comparateurs par colonne.
        /// <br/>Pendant le tri, si le comparateur n'existe pas dans ColumnsTypeComparer, CaseInsensitiveComparer est utilisé.
        /// </summary>
        public Dictionary<int, Type> ColumnsTypeComparer { get; set; }
    }
}

初始化ListView:

    <var>Visual.WIN.ctrlListView.OnShown</var>  : 
    eventSender.Columns.Clear();
    eventSender.SmallImageList = edvWinForm.ImageList16;
    eventSender.ListViewItemSorter = new ListViewColumnSorter();
    var col = eventSender.Columns.Add("Répertoire");
    col.Width = 160;
    col.ImageKey = "Domain";
    col = eventSender.Columns.Add("Fichier");
    col.Width = 180;
    col.ImageKey = "File";
    col = eventSender.Columns.Add("Date");
    col.Width = 120;
    col.ImageKey = "DateTime";
    eventSender.ListViewItemSorter.ColumnsTypeComparer.Add(col.Index, DateTime);
    col = eventSender.Columns.Add("Position");
    col.TextAlign = HorizontalAlignment.Right;
    col.Width = 80;
    col.ImageKey = "Num";
    eventSender.ListViewItemSorter.ColumnsTypeComparer.Add(col.Index, Int32);

填充 ListView:

<var>Visual.WIN.cmdSearch.OnClick</var>  : 
//non récursif et sans fonction
    ..ctrlListView:Items.Clear();
    ..ctrlListView:Sorting = SortOrder.None;
    var group = ..ctrlListView:Groups.Add(DateTime.Now.ToString()
                , Path.Combine(..cboDir:Text, ..ctrlPattern1:Text) + " contenant " + ..ctrlSearch1:Text);
    var perf =  Environment.TickCount;

    var files = new DirectoryInfo(..cboDir:Text).GetFiles(..ctrlPattern1:Text)
    var search = ..ctrlSearch1:Text;
    var ignoreCase = ..Search.IgnoreCase;
    //var result = new StringBuilder();
    var dirLength : int = ..cboDir:Text.Length;
    var position : int;
    var added : int = 0;
    for(var i : int = 0; i &lt; files.Length; i++){
        var file = files[i];
        if(search == ""
        || (position = File.ReadAllText(file.FullName).IndexOf(String(search)
                            , StringComparison(ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))) &gt; =0) {

        //  result.AppendLine(file.FullName.Substring(dirLength) + "\tPos : " + pkvFile.Value);
            var item = ..ctrlListView:Items.Add(file.FullName.Substring(dirLength));
            item.SubItems.Add(file.Name);
            item.SubItems.Add(File.GetLastWriteTime(file.FullName).ToString());
            item.SubItems.Add(position.ToString("# ### ##0"));
            item.Group = group;
            ++added;
        }
    }
    group.Header += " : " + added + "/" + files.Length + " fichier(s)"
                + "  en " + (Environment.TickCount - perf).ToString("# ##0 msec");

在 ListView 的列点击事件中:

<var>Visual.WIN.ctrlListView.OnColumnClick</var>  : 
// Déterminer si la colonne sélectionnée est déjà la colonne triée.
var sorter = eventSender.ListViewItemSorter;
if ( eventArgs.Column == sorter .SortColumn )
{
    // Inverser le sens de tri en cours pour cette colonne.
    if (sorter.Order == SortOrder.Ascending)
    {
        sorter.Order = SortOrder.Descending;
    }
    else
    {
        sorter.Order = SortOrder.Ascending;
    }
}
else
{
    // Définir le numéro de colonne à trier ; par défaut sur croissant.
    sorter.SortColumn = eventArgs.Column;
    sorter.Order = SortOrder.Ascending;
}

// Procéder au tri avec les nouvelles options.
eventSender.Sort();

上面使用了edvTools.TryParse函数。

class edvTools {
    /// <summary>
    /// Tente la conversion d'une valeur suivant un type EDVType
    /// </summary>
    /// <param name="pValue">Référence de la valeur à convertir</param>
    /// <param name="pType">Type EDV en sortie</param>
    /// <returns></returns>
    public static bool TryParse(ref object pValue, System.Type pType)
    {
        int lIParsed;
        double lDParsed;
        string lsValue;
        if (pValue == null) return false;
        if (pType.Equals(typeof(bool))) {
            bool lBParsed;
            if (pValue is bool) return true;
            if (double.TryParse(pValue.ToString(), out lDParsed)) {
                pValue = lDParsed != 0D;
                return true;
            }
            if (bool.TryParse(pValue.ToString(), out lBParsed)) {
                pValue = lBParsed;
                return true;
            }
            else
                return false;
        }
        if (pType.Equals(typeof(Double))) {
            if (pValue is Double) return true;
            if (double.TryParse(pValue.ToString(), out lDParsed)
                || double.TryParse(pValue.ToString().Replace(NumberDecimalSeparatorNOT, NumberDecimalSeparator), out lDParsed)) {
                pValue = lDParsed;
                return true;
            }
            else
                return false;
        }
        if (pType.Equals(typeof(int))) {
            if (pValue is int) return true;
            if (Int32.TryParse(pValue.ToString(), out lIParsed)) {
                pValue = lIParsed;
                return true;
            }
            else if (double.TryParse(pValue.ToString(), out lDParsed)) {
                pValue = (int)lDParsed;
                return true;
            }
            else
                return false;
        }
        if (pType.Equals(typeof(Byte))) {
            if (pValue is byte) return true;
            byte lByte;
            if (Byte.TryParse(pValue.ToString(), out lByte)) {
                pValue = lByte;
                return true;
            }
            else if (double.TryParse(pValue.ToString(), out lDParsed)) {
                pValue = (byte)lDParsed;
                return true;
            }
            else
                return false;
        }
        if (pType.Equals(typeof(long))) {
            long lLParsed;
            if (pValue is long) return true;
            if (long.TryParse(pValue.ToString(), out lLParsed)) {
                pValue = lLParsed;
                return true;
            }
            else if (double.TryParse(pValue.ToString(), out lDParsed)) {
                pValue = (long)lDParsed;
                return true;
            }
            else
                return false;
        }
        if (pType.Equals(typeof(Single))) {
            if (pValue is float) return true;
            Single lSParsed;
            if (Single.TryParse(pValue.ToString(), out lSParsed)
                || Single.TryParse(pValue.ToString().Replace(NumberDecimalSeparatorNOT, NumberDecimalSeparator), out lSParsed)) {
                pValue = lSParsed;
                return true;
            }
            else
                return false;
        }
        if (pType.Equals(typeof(DateTime))) {
            if (pValue is DateTime) return true;
            DateTime lDTParsed;
            if (DateTime.TryParse(pValue.ToString(), out lDTParsed)) {
                pValue = lDTParsed;
                return true;
            }
            else if (pValue.ToString().Contains("UTC")) //Date venant de JScript
            {
                if (_MonthsUTC == null) InitMonthsUTC();
                string[] lDateParts = pValue.ToString().Split(' ');
                lDTParsed = new DateTime(int.Parse(lDateParts[5]), _MonthsUTC[lDateParts[1]], int.Parse(lDateParts[2]));
                lDateParts = lDateParts[3].ToString().Split(':');
                pValue = lDTParsed.AddSeconds(int.Parse(lDateParts[0]) * 3600 + int.Parse(lDateParts[1]) * 60 + int.Parse(lDateParts[2]));
                return true;
            }
            else
                return false;

        }
        if (pType.Equals(typeof(Array))) {
            if (pValue is System.Collections.ICollection || pValue is System.Collections.ArrayList)
                return true;
            return pValue is System.Data.DataTable
                || pValue is string && (pValue as string).StartsWith("<");
        }
        if (pType.Equals(typeof(DataTable))) {
            return pValue is System.Data.DataTable
                || pValue is string && (pValue as string).StartsWith("<");

        }
        if (pType.Equals(typeof(System.Drawing.Bitmap))) {
            return pValue is System.Drawing.Image || pValue is byte[];

        }
        if (pType.Equals(typeof(System.Drawing.Image))) {
            return pValue is System.Drawing.Image || pValue is byte[];

        }
        if (pType.Equals(typeof(System.Drawing.Color))) {
            if (pValue is System.Drawing.Color) return true;
            if (pValue is System.Drawing.KnownColor) {
                pValue = System.Drawing.Color.FromKnownColor((System.Drawing.KnownColor)pValue);
                return true;
            }

            int lARGB;
            if (!int.TryParse(lsValue = pValue.ToString(), out lARGB)) {
                if (lsValue.StartsWith("Color [A=", StringComparison.InvariantCulture)) {
                    foreach (string lsARGB in lsValue.Substring("Color [".Length, lsValue.Length - "Color []".Length).Split(','))
                        switch (lsARGB.TrimStart().Substring(0, 1)) {
                            case "A":
                                lARGB = int.Parse(lsARGB.Substring(2)) * 0x1000000;
                                break;
                            case "R":
                                lARGB += int.Parse(lsARGB.TrimStart().Substring(2)) * 0x10000;
                                break;
                            case "G":
                                lARGB += int.Parse(lsARGB.TrimStart().Substring(2)) * 0x100;
                                break;
                            case "B":
                                lARGB += int.Parse(lsARGB.TrimStart().Substring(2));
                                break;
                            default:
                                break;
                        }
                    pValue = System.Drawing.Color.FromArgb(lARGB);
                    return true;
                }
                if (lsValue.StartsWith("Color [", StringComparison.InvariantCulture)) {
                    pValue = System.Drawing.Color.FromName(lsValue.Substring("Color [".Length, lsValue.Length - "Color []".Length));
                    return true;
                }
                return false;
            }
            pValue = System.Drawing.Color.FromArgb(lARGB);
            return true;
        }
        if (pType.IsEnum) {
            try {
                if (pValue == null) return false;
                if (pValue is int || pValue is byte || pValue is ulong || pValue is long || pValue is double)
                    pValue = Enum.ToObject(pType, pValue);
                else
                    pValue = Enum.Parse(pType, pValue.ToString());
            }
            catch {
                return false;
            }
        }
        return true;
    }
}

1
迟到了,这里简短介绍一下。它有以下限制:
  • 仅对SubItemsTexts进行普通字符串排序
  • 使用ListViewTag
  • 假设所有点击的列都已填充

您可以将任何ListView注册和注销到其服务中;确保Sorting设置为None..:

public static class LvSort
{
    static List<ListView> LVs = new List<ListView>();
    public static void registerLV(ListView lv)
    {
        if (!LVs.Contains(lv) && lv is ListView)
        {
            LVs.Add(lv);
            lv.ColumnClick +=Lv_ColumnClick;
        }
    }
    public static void unRegisterLV(ListView lv)
    {
        if (LVs.Contains(lv) && lv is ListView)
        {
            LVs.Remove(lv);
            lv.ColumnClick -=Lv_ColumnClick;
        }
    }

    private static void Lv_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        ListView lv = sender as ListView;
        if (lv == null) return;
        int c = e.Column;
        bool asc = (lv.Tag == null) || ( lv.Tag.ToString() != c+"");
        var items = lv.Items.Cast<ListViewItem>().ToList();
        var sorted =  asc ? items.OrderByDescending(x => x.SubItems[c].Text).ToList() :
                            items.OrderBy(x => x.SubItems[c].Text).ToList();
        lv.Items.Clear();
        lv.Items.AddRange(sorted.ToArray());
        if (asc) lv.Tag = c+""; else lv.Tag = null;
    }
}

要注册,只需执行以下操作:

public Form1()
{
    InitializeComponent();
    LvSort.registerLV(yourListView1);
}

更新:

这里有一个稍微扩展的版本,可以让你使用任何排序规则对各种数据类型进行排序。你需要做的就是为你的数据编写特殊的字符串转换函数,将其添加到函数列表中,并标记你的列。要这样做,只需在列的标签中附加标记字符串即可。

我已经添加了一个用于排序DataTimes和一个用于整数的函数。

此版本还将排序不规则的ListViews,即具有不同子项数量的ListViews。

public static class LvCtl
{
    static List<ListView> LVs = new List<ListView>();

    delegate string  StringFrom (string s);

    static Dictionary<string, StringFrom> funx = new Dictionary<string, StringFrom>();

    public static void registerLV(ListView lv)
    {
        if (!LVs.Contains(lv) && lv is ListView)
        {
            LVs.Add(lv);
            lv.ColumnClick +=Lv_ColumnClick;

            funx.Add("", stringFromString);
            for (int i = 0; i <  lv.Columns.Count; i++)
            {
                if (lv.Columns[i].Tag == null) continue;
                string n = lv.Columns[i].Tag.ToString();
                if (n == "") continue;
                if (n.Contains("__date")) funx.Add(n, stringFromDate);
                if (n.Contains("__int")) funx.Add(n, stringFromInt);
                else funx.Add(n, stringFromString);
            }

        }
    }

    static string stringFromString(string s)
    {
        return s;
    }
    static string stringFromInt(string s)
    {
        int i = 0;
        int.TryParse(s, out i);
        return i.ToString("00000") ;
    }
    static string stringFromDate(string s)
    {
        DateTime dt = Convert.ToDateTime(s);
        return dt.ToString("yyyy.MM.dd HH.mm.ss");
    }

    private static void Lv_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        ListView lv = sender as ListView;
        if (lv == null) return;


        int c = e.Column;
        string nt = lv.Columns[c].Tag != null ? lv.Columns[c].Tag.ToString() : "";
        string n = nt.Replace("__", "§").Split('§')[0];

        bool asc = (lv.Tag == null) || ( lv.Tag.ToString() != c+"");
        var items = lv.Items.Cast<ListViewItem>().ToList();
        var sorted =  asc?
            items.OrderByDescending(x =>  funx[nt]( c < x.SubItems.Count ?
                                    x.SubItems[c].Text: "")).ToList() :
            items.OrderBy(x => funx[nt](c < x.SubItems.Count ?
                          x.SubItems[c].Text : "")).ToList();
        lv.Items.Clear();
        lv.Items.AddRange(sorted.ToArray());
        if (asc) lv.Tag = c+""; else lv.Tag = null;
    }

    public static void unRegisterLV(ListView lv)
    {
        if (LVs.Contains(lv) && lv is ListView)
        {
            LVs.Remove(lv);
            lv.ColumnClick -=Lv_ColumnClick;
        }
    }
}

1

我能看到这个问题最初是在 5 年前发布的,当时程序员需要更加努力地工作才能获得他们想要的结果。 使用 Visual Studio 2012 及以上版本,一个懒惰的程序员可以进入“设计视图”来设置 Listview 属性,然后点击“属性”->“排序”,选择升序。 还有很多其他属性功能可供懒惰(也就是聪明)的程序员利用。


0
基于RedEye指出的示例,这里有一个需要更少代码的类:
它假设列总是以相同的方式排序,因此它在内部处理
ColumnClick事件接收器:

public class ListViewColumnSorterExt : IComparer {
    /// <summary>
    /// Specifies the column to be sorted
    /// </summary>
    private int ColumnToSort;
    /// <summary>
    /// Specifies the order in which to sort (i.e. 'Ascending').
    /// </summary>
    private SortOrder OrderOfSort;
    /// <summary>
    /// Case insensitive comparer object
    /// </summary>
    private CaseInsensitiveComparer ObjectCompare;

    private ListView listView;
    /// <summary>
    /// Class constructor.  Initializes various elements
    /// </summary>
    public ListViewColumnSorterExt(ListView lv) {
        listView = lv;
        listView.ListViewItemSorter = this;
        listView.ColumnClick += new ColumnClickEventHandler(listView_ColumnClick);

        // Initialize the column to '0'
        ColumnToSort = 0;

        // Initialize the sort order to 'none'
        OrderOfSort = SortOrder.None;

        // Initialize the CaseInsensitiveComparer object
        ObjectCompare = new CaseInsensitiveComparer();
    }

    private void listView_ColumnClick(object sender, ColumnClickEventArgs e) {
        ReverseSortOrderAndSort(e.Column, (ListView)sender);
    }

    /// <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;
        ListViewItem listviewX, listviewY;

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

        // Compare the two items
        compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);

        // 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>
    private int SortColumn {
        set {
            ColumnToSort = value;
        }
        get {
            return ColumnToSort;
        }
    }

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

    private void ReverseSortOrderAndSort(int column, ListView lv) {
        // Determine if clicked column is already the column that is being sorted.
        if (column == this.SortColumn) {
            // Reverse the current sort direction for this column.
            if (this.Order == SortOrder.Ascending) {
                this.Order = SortOrder.Descending;
            }
            else {
                this.Order = SortOrder.Ascending;
            }
        }
        else {
            // Set the column number that is to be sorted; default to ascending.
            this.SortColumn = column;
            this.Order = SortOrder.Ascending;
        }

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

假设您对排序选项满意,类属性为私有

您需要编写的唯一代码是:

在表单声明中

private ListViewColumnSorterExt listViewColumnSorter;  

在表单构造函数中

listViewColumnSorter = new ListViewColumnSorterExt(ListView1);  

...然后你就完成了。

那么,如何处理多个ListView的单个排序器呢?

public class MultipleListViewColumnSorter {
    private List<ListViewColumnSorterExt> sorters;

    public MultipleListViewColumnSorter() {
        sorters = new List<ListViewColumnSorterExt>();
    }

    public void AddListView(ListView lv) {
        sorters.Add(new ListViewColumnSorterExt(lv));
    }
}  

在表单声明中

private MultipleListViewColumnSorter listViewSorter = new MultipleListViewColumnSorter();  

在表单构造函数中

listViewSorter.AddListView(ListView1);  
listViewSorter.AddListView(ListView2);  
// ... and so on ...  

0

你可以使用像这样的手动排序算法

public void ListItemSorter(object sender, ColumnClickEventArgs e)
    {
        ListView list = (ListView)sender;
        int total = list.Items.Count;
        list.BeginUpdate();
        ListViewItem[] items = new ListViewItem[total];
        for (int i = 0; i < total; i++)
        {
            int count = list.Items.Count;
            int minIdx = 0;
            for (int j = 1; j < count; j++)
                if (list.Items[j].SubItems[e.Column].Text.CompareTo(list.Items[minIdx].SubItems[e.Column].Text) < 0)
                    minIdx = j;
            items[i] = list.Items[minIdx];
            list.Items.RemoveAt(minIdx);
        }
        list.Items.AddRange(items);
        list.EndUpdate();
    }

这个方法使用选择排序,时间复杂度为O^2,并按升序排列。您可以将'>'更改为'<'以进行降序排列,或者添加一个参数来使用此方法。它可以对任何被点击的列进行排序,并且适用于小量数据。


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