Сортировка ListView по столбцу - PullRequest
18 голосов
/ 10 октября 2009

В настоящее время я использую пользовательский сортировщик в просмотре списка и могу сортировать просмотр списка каждый раз, когда нажимаю на столбец FIRST, но он не сортируется по другим столбцам.

SortStyle: переменная, определяющая, сортировка по возрастанию или по убыванию.

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

Это прекрасно работает при сортировке по первому столбцу, но если бы вы делали это по любому другому столбцу, он бы просто сортировал по первому столбцу. Есть ли способ сортировки по нажатой колонке?

Ответы [ 14 ]

23 голосов
/ 25 марта 2010

Забудьте о вашем обычном сортировщике. Начните заново, используя код на следующей странице. Он покажет вам, как определить класс, который наследуется от интерфейса IComparer. Каждая строка закомментирована, так что вы можете увидеть, что происходит. Единственное потенциальное осложнение - это то, как вы извлекаете элементы Listview из элемента управления Listview. Уберите их в квадрат, и все, что вам нужно сделать, это скопировать и вставить класс интерфейса IComparer и метод columnClick.

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

19 голосов
/ 12 октября 2009

Если вы начинаете с ListView, сделайте себе огромное одолжение и используйте взамен ObjectListView . ObjectListView - это оболочка с открытым исходным кодом для .NET WinForms ListView, которая значительно упрощает использование ListView и решает множество типичных для вас проблем. Сортировка по щелчку по столбцу - это одна из многих функций, которые она обрабатывает автоматически.

Серьезно, вы никогда не пожалеете об использовании ObjectListView вместо обычного ListView.

6 голосов
/ 10 октября 2009

Я сортирую, используя имя столбца, чтобы установить любые особенности сортировки, которые, возможно, потребуется обработать, основываясь на типе данных, хранящихся в столбце, или если столбец уже отсортирован (asc / desc). Вот фрагмент из моего обработчика событий 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;
        }
    }
}
3 голосов
/ 10 сентября 2014

Внесены незначительные изменения в статью здесь для удобства сортировки строковых и числовых значений в 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 голосов
/ 13 марта 2012

Мое решение - это класс для сортировки элементов 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 голос
/ 10 июня 2017

Поздно на вечеринку, вот короткая. У него есть следующие ограничения:

  • Он выполняет только обычную строковую сортировку SubItems 'Texts
  • Используется ListView Tag
  • Предполагается, что все выбранные столбцы будут заполнены

Вы можете зарегистрировать и отменить регистрацию любого 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 и один для целых чисел.

Эта версия также будет сортировать неровные ListView, то есть те, которые имеют разное количество подэлементов.

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 голос
/ 29 января 2015

Я вижу, что этот вопрос был первоначально опубликован 5 лет назад, когда программистам приходилось работать усерднее, чтобы получить желаемые результаты. В Visual Studio 2012 и более поздних версиях ленивый программист может перейти к представлению «Дизайн» для параметров свойств ListView и нажать «Свойства» -> «Сортировка», выбрать «По возрастанию». Существует множество других свойств, позволяющих получить различные результаты, которые может использовать ленивый (умный) программист.

0 голосов
/ 20 февраля 2019

я использовал этот трюк

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;
            }
        }
0 голосов
/ 04 ноября 2016

Я немного изменил пример от Microsoft: https://support.microsoft.com/en-us/kb/319401

Этот метод сортирует только один раз для сортировки по возрастанию. Мои модификации делают это сортировать в обе стороны.

public class ListViewItemComparer : IComparer
{
    private int col;
    bool bAsc = false;
    public ListViewItemComparer()
    {
        col = 0;
    }
    public ListViewItemComparer(int column, bool b)
    {
        col = column;
        bAsc = b;
    }
    public int Compare(object x, object y)
    {
        if (bAsc)
        {
            return String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text);
            bAsc = false;
        }
        else
        {
            return String.Compare(((ListViewItem)y).SubItems[col].Text, ((ListViewItem)x).SubItems[col].Text);
            bAsc = true;
        }
    }

}

Затем я создаю объект этого класса при каждом щелчке заголовка столбца

        bool sortAscending = false;
        private void inventoryList_ColumnClick(object sender, ColumnClickEventArgs e)
        {

            if (!sortAscending)
            {
                sortAscending = true;
            }
            else
            {
                sortAscending = false;
            }
            this.inventoryList.ListViewItemSorter = new ListViewItemComparer(e.Column, sortAscending);

        }
0 голосов
/ 23 ноября 2014

Основываясь на примере, указанном 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 .

Единственный код, который вам нужно написать:

в декларации формы

private ListViewColumnSorterExt listViewColumnSorter;  

в конструкторе форм

listViewColumnSorter = new ListViewColumnSorterExt(ListView1);  

... и все готово.

А как насчет одного сортировщика, который обрабатывает несколько ListViews?

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 ...  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...