2009-10-10 73 views
16

Actualmente utilizo un clasificador personalizado en la vista de lista, y puedo ordenar la vista de lista cada vez que hago clic en la PRIMERA columna, pero no se ordena por otras columnas.Ordenando un ListView por columna

SortStyle: Variable para determinar si es Ascending Sort o Descending.

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

Esto funciona bien cuando se ordenan por la primera columna, pero si tuviera que hacerlo en cualquier otra columna, sería sólo una especie por la primera columna. ¿Hay alguna manera de ordenar por clic en la columna?

+0

Mike si miras a mi ejemplo a continuación creo que estás buscando el valor ColumnClickEventArgs.Column. Le dirá en qué encabezado de columna se hizo clic. –

Respuesta

20

Si usted está comenzando con un ListView, hazte un gran favor y usa un ObjectListView en su lugar. ObjectListView es un contenedor de código abierto alrededor de .NET WinForms ListView, que hace que ListView sea mucho más fácil de usar y soluciona muchos problemas comunes para usted. Ordenar por clic de columna es una de las muchas cosas que maneja automáticamente.

En serio, nunca se arrepentirá de utilizar un ObjectListView en lugar de un ListView normal.

+10

He utilizado ObjectListView antes y me ha gustado. Lo único que debe tener en cuenta es que es GPL, por lo que puede tener que liberar su código fuente. – Slapout

+0

* puede * tener que? ¿Alguien sabe si podemos usar GPL o no? Lo que realmente quiero saber es: ¿puedo usarlo legalmente en un software comercial vendido y no de código abierto? (en Europa y EE. UU.) – noelicus

+0

Si desea utilizar esto en una aplicación de código cerrado, puede comprar una licencia (barata): http://objectlistview.sourceforge.net/cs/faq.html#can-i-use- objectlistview-in-a-commercial-application – Grammarian

5

Ordeno usar el nombre de la columna para configurar las especificaciones de clasificación que pueden necesitar ser manejadas según el tipo de datos almacenados en la columna y si la columna ya se ha ordenado (asc/desc). Aquí hay un fragmento de mi controlador de eventos 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; 
    } 

A continuación es mi 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; 
     } 
    } 
} 
+0

Esta página proporciona una solución similar -> http://www.java2s.com/Tutorial/CSharp/0460__GUI-Windows-Forms/ListViewSorter.htm –

+0

Al crear su ListViewitemComparer basado en el enlace que proporcioné anteriormente, deberá agregar este código para obtener la clasificación Ascendente/Descendente: string itemXText = itemX.SubItems [Column] .Text; string itemYText = itemY.SubItems [Column] .Text; // clasificación si (itemX.ListView.Sorting == SortOrder.Ascending) { retorno String.Compare (itemXText, itemYText); } else { return String.Compare (itemYText, itemXText); } –

+0

@ will-p ¿Podría mostrar a qué se parece ListViewItemComparer? – slayernoah

0

yo recomendaría usted para dataGridView, para la materia pesada .. es incluir una gran cantidad de característica de auto listviwe no lo hace

0

Utilice ListView.SortExpression.

Cuando se ordenan varias columnas, esta propiedad contiene una lista separada por comas de los campos a ordenar.

+0

Se trata de ASP.NET Web Controls, no de Windows Forms. – ygoe

21

Olvídate de tu clasificador personalizado. Comience de nuevo usando el código en la página siguiente. Le mostrará cómo definir una clase que hereda de la interfaz IComparer. Cada línea está comentada, por lo que puedes ver lo que está sucediendo. La única complicación potencial es cómo está recuperando sus elementos Listview de su control Listview. Obtenga los cuadrados y todo lo que necesita hacer es copiar y pegar la clase de interfaz IComparer y el método columnClick.

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

+2

Enlace muy útil. Parece ser la solución más fácil. Gracias RedEye. – Martin

+1

O mejor aún, use el código vinculado arriba para crear su propia Lista ordenable, entonces todo lo que necesita cambiar es una línea donde se inicializa su lista. –

+1

Dado que el código en el receptor de eventos es un comportamiento bastante estándar, Agregué un método ** ReverseSortOrderAndSort (int column, ListView lv) ** a la clase ** ListViewColumnSorter ** , por lo que el código en el receptor del evento pasa a ser: privado void listView1_ColumnClick (remitente del objeto, ColumnClickEventArgs e) { listViewColumnSorter.ReverseSortOrderAndSort (e.Column, (ListView) remitente); } –

2

Mi solución es una clase para ordenar los elementos ListView cuando se hace clic sobre el encabezado de la columna.

Puede especificar el tipo de cada columna.

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

Eso es todo!

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

La inicialización de un 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); 

Llenar un 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"); 

En la columna ListView clic:

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

Función edvTools.TryParse utilizado anteriormente

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

Se puede utilizar un algoritmo de clasificación manual como este

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

Este método utiliza la selección especie en O^2 orden y como ascendente. Puede cambiar '>' con '<' para un descenso o agregar un argumento para este método. Clasifica cualquier columna que se haga clic y funciona perfecto para una pequeña cantidad de datos.

0

Dado que este sigue siendo un tema que se ve desde arriba, pensé que podría tener en cuenta que se me ocurrió una solución dinámica para ordenar la lista por columna. Aquí está el código por si acaso alguien más quiere usarlo también. Simplemente implica enviar los elementos de la lista a una tabla de datos, ordenar la vista predeterminada de la tabla de datos por el nombre de la columna (usando el índice de la columna en la que se hace clic) y luego sobrescribir la tabla con el método defaultview.totable(). Luego simplemente agrégalos a la vista de lista. Y wa la, es una lista ordenada por columna.

public void SortListView(int Index) 
    { 
     DataTable TempTable = new DataTable(); 
     //Add column names to datatable from listview 
     foreach (ColumnHeader iCol in MyListView.Columns) 
     { 
      TempTable.Columns.Add(iCol.Text); 
     } 
     //Create a datarow from each listviewitem and add it to the table 
     foreach (ListViewItem Item in MyListView.Items) 
     { 
      DataRow iRow = TempTable.NewRow(); 
      // the for loop dynamically copies the data one by one instead of doing irow[i] = MyListView.Subitems[1]... so on 
      for (int i = 0; i < MyListView.Columns.Count; i++) 
      { 
       if (i == 0) 
       { 
        iRow[i] = Item.Text; 
       } 
       else 
       { 
        iRow[i] = Item.SubItems[i].Text; 
       } 
      } 
      TempTable.Rows.Add(iRow); 
     } 
     string SortType = string.Empty; 
     //LastCol is a public int variable on the form, and LastSort is public string variable 
     if (LastCol == Index) 
     { 
      if (LastSort == "ASC" || LastSort == string.Empty || LastSort == null) 
      { 
       SortType = "DESC"; 
       LastSort = "DESC"; 
      } 
      else 
      { 
       SortType = "ASC"; 
       LastSort = "ASC"; 
      } 
     } 
     else 
     { 
      SortType = "DESC"; 
      LastSort = "DESC"; 
     } 
     LastCol = Index; 
     MyListView.Items.Clear(); 
     //Sort it based on the column text clicked and the sort type (asc or desc) 
     TempTable.DefaultView.Sort = MyListView.Columns[Index].Text + " " + SortType; 
     TempTable = TempTable.DefaultView.ToTable(); 
     //Create a listview item from the data in each row 
     foreach (DataRow iRow in TempTable.Rows) 
     { 
      ListViewItem Item = new ListViewItem(); 
      List<string> SubItems = new List<string>(); 
      for (int i = 0; i < TempTable.Columns.Count; i++) 
      { 
       if (i == 0) 
       { 
        Item.Text = iRow[i].ToString(); 
       } 
       else 
       { 
        SubItems.Add(iRow[i].ToString()); 
       } 
      } 
      Item.SubItems.AddRange(SubItems.ToArray()); 
      MyListView.Items.Add(Item); 
     } 
    } 

Este método es dinámica, ya que utiliza el nombre de columna existente y no requiere conocer el índice o el nombre de cada columna o incluso el número de columnas están en la vista de lista/tabla de datos. Puede invocarlo creando un evento para listview.columnclick y luego SortListView (e.column).

1

Se realizaron cambios menores en el artículo here para acomodar la ordenación de cadena y valores numéricos en ListView.

Form1.cs contiene

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 contiene

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

} 
0

Basado en el ejemplo señalado por los ojos rojos, aquí es una clase que necesita menos código:
asume que las columnas son siempre ordenados de la misma manera, por lo que maneja el receptor de sucesos
ColumnClick internamente:

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

Suponiendo que estés satisfecho con las opciones de clasificación, las propiedades de la clase son privada.

El único código que necesita para escribir es:

en declaraciones Forma

private ListViewColumnSorterExt listViewColumnSorter; 

en el Formulario constructor

listViewColumnSorter = new ListViewColumnSorterExt(ListView1); 

... y ya está.

¿Y un clasificador único que maneje múltiples ListViews?

public class MultipleListViewColumnSorter { 
    private List<ListViewColumnSorterExt> sorters; 

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

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

en declaraciones Forma

private MultipleListViewColumnSorter listViewSorter = new MultipleListViewColumnSorter(); 

en el Formulario constructor

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

puedo ver que esta cuestión fue publicado originalmente hace 5 años cuando los programadores tenían que trabajar más para obtener sus resultados deseados Con Visual Studio 2012 y versiones posteriores, un programador perezoso puede ir a la Vista de diseño para la configuración de propiedades de Listview y hacer clic en Propiedades-> Clasificación, elegir Ascendente. Existen muchas otras propiedades para obtener los diversos resultados que un programador perezoso (también conocido como inteligente) puede aprovechar.

0

he modificado ligeramente el ejemplo de Microsoft: https://support.microsoft.com/en-us/kb/319401

Este método único tipo una vez para orden ascendente. Mis modificaciones lo hacen ordenar en ambos sentidos.

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

} 

Luego de crear un objeto de esta clase cada vez que se hace clic en un encabezado de columna

 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

tarde a la fiesta, aquí es corta. Cuenta con las siguientes limitaciones:

  • Sólo se hace una cadena sencilla especie de la SubItems 'Texts
  • que utiliza el ListView' s Tag
  • Se asume toda hace clic columnas se llenaron

Puede registrar & anular el registro de cualquier ListView a su servicio; asegúrese de que el Sorting se establece en 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; 
    } 
} 

registro, Simplemente hacer ..:

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

Actualización:

Aquí es una versión ligeramente extendida que le permitirá ordenar todas las clases de tipos de datos utilizando una regla de clasificación que ocurra. Todo lo que necesita hacer es escribir una conversión de cadena especial para sus datos, agregarla a la lista de funciones y marcar sus columnas. Para hacerlo, simplemente coloque los nombres de las columnas anexados con una cadena de marcador en las etiquetas de las columnas.

He agregado uno para ordenar DataTimes y uno para enteros.

Esta versión también ordenará ListViews irregulares, es decir, aquellos con diferentes números de subelementos.

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