2009-10-22 16 views
2

Avec les contrôles ListView, vous pouvez spécifier une colonne à trier et une méthode sort() à votre guise.Tri d'une ListView par plusieurs colonnes en C#

Cependant, cela ne permet que le tri sur une seule colonne. J'ai envie de trier par exemple, Colonne A d'abord, puis par Colonne F quand ils sont identiques.

J'ai trouvé quelques classes de comparaison personnalisées écrites en ligne, mais je me suis demandé si stackoverflow pouvait être plus clair. De plus, avoir ceci ici peut aider les autres à le chercher à l'avenir :)

Toutes les suggestions ou des exemples sur la façon de s'y prendre ont apprécié.

+1

est ce que vous remplissez cette listview via une requête? Peut-être que vous pourriez enregistrer les critères de tri dans un champ caché, puis simplement classer selon ces critères dans votre requête. – jim

+0

Malheureusement non. Les données vont dans la liste de visualisation partiellement peuplée, et alors que plus d'informations sur les articles sont connus, la liste est mise à jour - nécessitant le recours alors que dans la liste comme ils viennent ... –

Répondre

5

Donc, après avoir joué, la réponse que j'ai trouvée était d'écrire une classe ListViewItemComparer via l'interface IComparer. J'ai ensuite écrasé la méthode Compare(), et je pouvais maintenant retourner -1, 0, ou 1 selon la comparaison entre la première colonne primaire, puis, lorsqu'elle était égale, la colonne secondaire.

Tout à fait bien rangé à la fin, je pense.

+0

pouvez-vous partager votre ListViewItemComparer classe ici? Je suis confronté au même problème et à la recherche d'une solution pour trier mon ListView – Dennis

+0

@Dennis désolé, en rétrospective j'aurais dû élargir ma réponse avec un exemple de code à l'époque, mais c'était plusieurs entreprises agao et ne l'ont plus. Mes excuses. –

+0

Cela ne semble pas trop banal; la méthode de comparaison réelle n'obtient jamais toute la portée, seulement deux objets à comparer. – Nyerguds

0

Est-ce que c'est sur le Web ou Winform? Sur le web, vous pouvez mettre en place une expression qui a les colonnes, séparées par des virgules, et pass it to the sort() method of the listview

Framework 3.5 et si vous ...

+0

Windows Form, merci bien –

0

Eh bien, si vous voulez juste les colonnes à trier, essayer Liste de la liste; par exemple comme suit:

List<List<string>> lstColumns = new List<List<string>>(); 

N'avez pas essayé, mais juste une solution rapide.

2

Comme avec presque toutes les tâches, ObjectListView (un wrapper open source autour de .NET WinForms ListView) rend la vie avec un ListView beaucoup plus facile. ObjectListView a SecondarySortColumn et SecondarySortOrder propriétés pour faire exactement ce que vous demandez.

Si vous souhaitez effectuer un tri plus uniforme, vous pouvez installer un CustomSorter. Jetez un oeil à this recipe

2

@MarkMayo, j'avais créé ma propre classe de tri ListViewItemComparer via l'interface IComparer qui supporte le tri des colonnes secondaires/prioritaires.

Je écraser Compare() méthode pour prendre en charge numérique, date & comparaison de chaîne insensible à la casse.

Il va d'abord trier la colonne que vous souhaitez, si les deux valeurs comparées sont les mêmes, il prendra la deuxième colonne comme référence pour le tri, donc le tri secondaire.

Vous avez juste besoin d'inclure cette classe de trieur et de modifier l'événement Listview ColumnClick du formulaire avec l'exemple VB suivant.Code net:

ListViewItemComparer Classe

Imports System.Collections 

''' <summary> 
''' This class is an implementation of the 'IComparer' interface. 
''' </summary> 
Public Class ListViewColumnSorter 
    Implements IComparer 
    ''' <summary> 
    ''' Specifies the column to be sorted 
    ''' </summary> 
    Private ColumnToSort As Integer 
    ''' <summary> 
    ''' Specifies the secondary column to be sorted 
    ''' </summary> 
    Private SecondaryColumnToSort As Integer = -1 
    ''' <summary> 
    ''' Specifies the order in which to sort (i.e. 'Ascending'). 
    ''' </summary> 
    Private OrderOfSort As SortOrder 
    ''' <summary> 
    ''' Class constructor. Initializes various elements 
    ''' </summary> 
    Public Sub New(ByVal column_number As Integer, ByVal sort_order As SortOrder) 
     ColumnToSort = column_number 
     OrderOfSort = sort_order 
    End Sub 
    ''' <summary> 
    ''' Class constructor. Initializes various elements 
    ''' </summary> 
    Public Sub New(ByVal column_number As Integer, ByVal sort_order As SortOrder, ByVal secondary_column_number As Integer) 
     ColumnToSort = column_number 
     SecondaryColumnToSort = secondary_column_number 
     OrderOfSort = sort_order 
    End Sub 

    ''' <summary> 
    ''' This method is inherited from the IComparer interface. It compares the two objects passed and support secondary column 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 Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare 
     Dim compareResult As Integer 
     Dim listviewX As ListViewItem, listviewY As ListViewItem 

     ' Cast the objects to be compared to ListViewItem objects 
     listviewX = DirectCast(x, ListViewItem) 
     listviewY = DirectCast(y, ListViewItem) 

     ' Compare the two items 
     Dim x1 As Object = listviewX.SubItems(ColumnToSort) 
     Dim y1 As Object = listviewY.SubItems(ColumnToSort) 

     ' Use .tag for comparison if not empty 
     If (x1.Tag IsNot vbNullString) And (y1.Tag IsNot vbNullString) Then 
      compareResult = ObjectComparer(x1.Tag, y1.Tag) 
     Else 
      compareResult = ObjectComparer(x1.Text, y1.Text) 
     End If 

     'require secondary column compare? 
     If (compareResult = 0 And SecondaryColumnToSort >= 0 And SecondaryColumnToSort <> ColumnToSort) Then 
      ' Compare the two items 
      Dim x2 As Object = listviewX.SubItems(SecondaryColumnToSort) 
      Dim y2 As Object = listviewY.SubItems(SecondaryColumnToSort) 

      ' Use .tag for comparison if not empty 
      If (x2.Tag IsNot vbNullString) And (y2.Tag IsNot vbNullString) Then 
       compareResult = ObjectComparer(x2.Tag, y2.Tag) 
      Else 
       compareResult = ObjectComparer(x2.Text, y2.Text) 
      End If 
     End If 

     ' Calculate correct return value based on object comparison 
     If OrderOfSort = SortOrder.Ascending Then 
      ' Ascending sort is selected, return normal result of compare operation 
      Return compareResult 
     ElseIf OrderOfSort = SortOrder.Descending Then 
      ' Descending sort is selected, return negative result of compare operation 
      Return (-compareResult) 
     Else 
      ' Return '0' to indicate they are equal 
      Return 0 
     End If 
    End Function 

    ''' <summary> 
    ''' This method compares the two objects passed. Object supported are numeric, dates and string 
    ''' </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> 
    Private Function ObjectComparer(x As Object, y As Object) As Integer 
     Dim compareResult As Integer 

     If IsNumeric(x) And IsNumeric(y) Then 'comparing numbers 
      compareResult = Val(x).CompareTo(Val(y)) 
     ElseIf IsDate(x) And IsDate(y) Then 'comparing dates 
      compareResult = DateTime.Parse(x).CompareTo(DateTime.Parse(y)) 
     Else 'comparing string 
      Dim ObjectCompare As New CaseInsensitiveComparer 
      compareResult = ObjectCompare.Compare(x.ToString, y.ToString) 
     End If 
     Return compareResult 
    End Function 

End Class 

Listview de Windows Form ColumnClick

Private prevColumnClick As Integer 'to store previous sorted column number 
Private secondary_column_to_sort As Integer = 0 'column 0 

Private Sub lvLog_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles lvLog.ColumnClick 
    Dim myListView As ListView = DirectCast(sender, ListView) 
    Dim sort_order As System.Windows.Forms.SortOrder 

    If myListView.Columns(e.Column).Tag Is Nothing Then 
     sort_order = SortOrder.Ascending 
    Else 
     ' Get previous sort order information from columns .tag 
     sort_order = DirectCast(myListView.Columns(e.Column).Tag, System.Windows.Forms.SortOrder) 
    End If 

    If (prevColumnClick = e.Column) Then 
     If sort_order = SortOrder.Ascending Then 
      sort_order = SortOrder.Descending 
     Else 
      sort_order = SortOrder.Ascending 
     End If 
    End If 

    ' Initialize ColumnSorter class 
    myListView.ListViewItemSorter = New ListViewColumnSorter(e.Column, sort_order, secondary_column_to_sort) 

    ' Perform the sort with these new sort options. 
    'myListView.Sort() 

    ' Store current column sorting order 
    myListView.Columns(e.Column).Tag = sort_order 

    ' Store previous column number clicked 
    prevColumnClick = e.Column 

End Sub 
1

Il est sans doute pas la façon la plus efficace, mais vous pouvez juste faire ce qui suit:

listView.Sort(5); // Column F, then 
listView.Sort(0); // Column A 

Notez l'ordre inverse.

Questions connexes