Listview, ¿ordenar el contenido al clickar sobre una columna?

Iniciado por Eleкtro, 31 Diciembre 2012, 13:40 PM

0 Miembros y 1 Visitante están viendo este tema.

Eleкtro

¿Me pueden decir alguna manera sencilla para ordenar alfabéticamente la columna de "problem" al pulsar click sobre el nombre de la columna?

O al menos, ¿Como se llama el evento que debo manejar? :S

Gracias.









seba123neo

hay varias maneras pero una de las mejores es usar la interface llamada IComparer. aca te paso la clase que usaba:

Código (vbnet) [Seleccionar]
Public Class COrdenarListview
    Implements IComparer

    Private vIndiceColumna As Integer
    Private vTipoOrden As SortOrder

    Public Sub New(ByVal pIndiceColumna As Integer, ByVal pTipoOrden As SortOrder)
        vIndiceColumna = pIndiceColumna
        vTipoOrden = pTipoOrden
    End Sub

    Public Function Ordenar(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
        Dim item_x As ListViewItem = DirectCast(x, ListViewItem)
        Dim item_y As ListViewItem = DirectCast(y, ListViewItem)

        Dim string_x As String

        If item_x.SubItems.Count <= vIndiceColumna Then
            string_x = ""
        Else
            string_x = item_x.SubItems(vIndiceColumna).Text
        End If

        Dim string_y As String
        If item_y.SubItems.Count <= vIndiceColumna Then
            string_y = ""
        Else
            string_y = item_y.SubItems(vIndiceColumna).Text
        End If

        If vTipoOrden = SortOrder.Ascending Then
            If IsNumeric(string_x) And IsNumeric(string_y) Then
                Return Val(string_x).CompareTo(Val(string_y))
            ElseIf IsDate(string_x) And IsDate(string_y) Then
                Return DateTime.Parse(string_x).CompareTo(DateTime.Parse(string_y))
            Else
                Return String.Compare(string_x, string_y)
            End If
        Else
            If IsNumeric(string_x) And IsNumeric(string_y) Then
                Return Val(string_y).CompareTo(Val(string_x))
            ElseIf IsDate(string_x) And IsDate(string_y) Then
                Return DateTime.Parse(string_y).CompareTo(DateTime.Parse(string_x))
            Else
                Return String.Compare(string_y, string_x)
            End If
        End If
    End Function
End Class


luego para usarla tenes que usar el evento ColumnClick de esta forma:

Código (vbnet) [Seleccionar]
Private Sub lv_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles lv.ColumnClick

        Dim vIndiceColumna As ColumnHeader = lv.Columns(e.Column)

        Dim vTipoOrden As System.Windows.Forms.SortOrder

        If vColumnaOrden Is Nothing Then
            vTipoOrden = SortOrder.Ascending
            vOrden = SortOrder.Ascending
        Else
            If vIndiceColumna.Equals(vColumnaOrden) Then
                If vOrden = SortOrder.Ascending Then
                    vTipoOrden = SortOrder.Descending
                    vOrden = SortOrder.Descending
                Else
                    vTipoOrden = SortOrder.Ascending
                    vOrden = SortOrder.Ascending
                End If
            Else
                vTipoOrden = SortOrder.Ascending
                vOrden = SortOrder.Ascending
            End If
        End If

        vColumnaOrden = vIndiceColumna

        lv.ListViewItemSorter = New COrdenarListview(e.Column, vTipoOrden)
        lv.Sort()
    End Sub


esta lo que hace es que te "detecta" por asi decirlo, el tipo de datos de la columna y te lo ordena dependiendo ese tipo de dato, por ejemplo si tenes numeros te lo ordena como numero y no como string, te detecta las fechas y los strings comunes, y tiene los 2 metodos ascendente y descendente.

digo "usaba", porque desde hace rato ya en .NET uso el ObjectListview, es lejos el mejor control creado para .NET, el cual te facilita todo, es mil veces mejor que el Listview de .NET, y tambien te hace todo automatico el tema del ordenamiento. te ahorras todo estos temas de ensuciar el codigo con cada cosita extra que uno necesita hacer.

saludos.
La característica extraordinaria de las leyes de la física es que se aplican en todos lados, sea que tú elijas o no creer en ellas. Lo bueno de las ciencias es que siempre tienen la verdad, quieras creerla o no.

Neil deGrasse Tyson

Eleкtro

A mi me gustaría usar el objectlistview pero no tengo ni idea de como añadir un item,
Seba123Neo, ¿Serías tán amable de enseñarme como crear/agregar un objeto para añadirlo al ObjectListView?, es decir, un item. ¿?
Lo intenté hacer pero es distinto de hacer como se haría con el ListView normal.

PD: Gracias por la class.








seba123neo

#3
en el objectlistview los items no se agregan como en el comun, justamente se llama "object" porque se maneja todo con objetos, o sea tenes que crear una clase con las propiedades (que serian las columnas) que va a tener el listview y entonces despues asignas esa clase al listview y automaticamente te lo detecta y se arma solo. para llenar items simplemente creas una lista de las clase es que creas y las vas llenado con datos,  cada clase despues seria un registro. si te bajas este control ya viene con un proyecto de como se usa, esta en C# pero se entiende bien la logica.

despues creo un post mostrando como se usa.
La característica extraordinaria de las leyes de la física es que se aplican en todos lados, sea que tú elijas o no creer en ellas. Lo bueno de las ciencias es que siempre tienen la verdad, quieras creerla o no.

Neil deGrasse Tyson

Eleкtro

Cita de: seba123neo en 31 Diciembre 2012, 16:53 PMdespues creo un post mostrando como se usa.

Deseando ver el mini-tutorial ;D, nos servirá a todos, además es un magnifico control.

PD: La explicación se me ha quedado corta XD, de todas formas lo intentaré a ver...

Saludos








spiritdead

Cita de: EleKtro H@cker en 31 Diciembre 2012, 17:28 PM
Deseando ver el mini-tutorial ;D, nos servirá a todos, además es un magnifico control.

PD: La explicación se me ha quedado corta XD, de todas formas lo intentaré a ver...

Saludos

si lo necesitas urgente, avisame y por teamviewver se te enseña rapido, dificil no es XD
Facilitador De Tareas - Task Simplifier (FDT)

Eleкtro

#6
Cita de: spiritdead en  1 Enero 2013, 00:24 AM
si lo necesitas urgente, avisame y por teamviewver se te enseña rapido, dificil no es XD

Gracias spiritdead pero no me urge, aún estoy aprendiendo a usar el listview convencional.




@seba123Neo

¿Como lo hago funcionar?

Me da error en las variables:
vColumnaOrden
   vOrden

Dicen que no están declaradas, y no se con que tipo de valor debo setearlas ni nada xD

Código (vbnet) [Seleccionar]
 Private Sub GListView_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles GListView.ColumnClick

       Dim vIndiceColumna As ColumnHeader = GListView.Columns(e.Column)

       Dim vTipoOrden As System.Windows.Forms.SortOrder

       If vColumnaOrden Is Nothing Then
           vTipoOrden = SortOrder.Ascending
           vOrden = SortOrder.Ascending
       Else
           If vIndiceColumna.Equals(vColumnaOrden) Then
               If vOrden = SortOrder.Ascending Then
                   vTipoOrden = SortOrder.Descending
                   vOrden = SortOrder.Descending
               Else
                   vTipoOrden = SortOrder.Ascending
                   vOrden = SortOrder.Ascending
               End If
           Else
               vTipoOrden = SortOrder.Ascending
               vOrden = SortOrder.Ascending
           End If
       End If

       vColumnaOrden = vIndiceColumna

       GListView.ListViewItemSorter = New COrdenarListview(e.Column, vTipoOrden)
       GListView.Sort()
   End Sub





EDITO:
Seba123Neo, si no he captado mal la idea, al final lo he hecho así, y funciona bien, pero no sé si es peor que tu snippet:



Código (vbnet) [Seleccionar]
   
' En las declaraciones...
Dim ColumnOrder As String = "Down"


Private Sub GListView_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles GListView.ColumnClick
       If ColumnOrder = "Down" Then
           Me.GListView.ListViewItemSorter = New COrdenarListview(e.Column, SortOrder.Ascending)
           GListView.Sort()
           ColumnOrder = "Up"
       ElseIf ColumnOrder = "Up" Then
           Me.GListView.ListViewItemSorter = New COrdenarListview(e.Column, SortOrder.Descending)
           GListView.Sort()
           ColumnOrder = "Down"
       End If
   End Sub








phetete

estimados:

he probado el codigo fuente y tambien otro muy similar de el guille, y ambos me resultan muy lentos. aproximadamente 30 segundos para una tabla 30 filas y 2 columnas...!!!

al hacer clic en la primera columna que solo tiene numeros no hay problema, pero al ordenar por la segunda columna, que es texto, se demora demasiado.

alguna sugerencia... pese a que han pasado pvarios años desde el ultimo post.

pd: estoy trabajando en vb.net 2010.

saludos.

Eleкtro

#8
Cita de: phetete en 12 Enero 2016, 21:00 PMalguna sugerencia...

El código del compañero @seba123Neo y cualquier comparer en general no tiene misterio, se implementa la interfáz IComparer o un type que la implemente (ej. para texto: CaseInsensitiveComparer) y se implementan los métodos base, es el modelo standard a seguir para implementar dicha funcionalidad de manera correcta, así que si te tarda digamos más de 2 segundos en ordenar entonces seguramente el problema es ajeno al comparer.

· ¿Tal vez estés utilizando un listview de terceros que sea muy "pesado", o una cantidad de datos muy "pesada"?.

· Si las circunstancias lo permiten siempre puedes probar a utilizar el modo virtual para aumentar en gran medida el rendimiento del listview:
De todas formas, eso es solo una sugerencia adicional y ajena al problema real de los 30 segundos de ordenamiento.

· Revisa los controladores de eventos de tu listview para verificar si estás haciendo algo incorrecto con los items que provoque ese exceso de tiempo en el ordenamiento... y publica el código relevante, de lo contrario dudo mucho que alguien te pueda ayudar, ya que como digo el problema seguramente no sea del comparer (a menos que la lógica del comparer que estés usando sea tan incorrecta como para provocar ese tipo de problema).

Te sugiero reemplazar la lógica de ordenamiento que estés utilizando y probar con el siguiente código fuente extraido de la próxima versión (sin publicar todavía) de mi API ElektroKit:




Ejemplo de uso:
Código (vbnet) [Seleccionar]
Public Class Form1 : Inherits Form

   Friend WithEvents MyListView As New ListView
   Private sorter As New ListViewColumnSorter

   Public Sub New()

       MyClass.InitializeComponent()

       With Me.MyListView

           ' Set the sorter, our ListViewColumnSorter.
           .ListViewItemSorter = sorter

           ' The initial direction for the sorting.
           .Sorting = SortOrder.Ascending

           ' Set the initial sort-modifier.
           sorter.SortModifier = SortModifiers.SortByText

           ' Add some columns.
           .Columns.Add("Text").Tag = SortModifiers.SortByText
           .Columns.Add("Numbers").Tag = SortModifiers.SortByNumber
           .Columns.Add("Dates").Tag = SortModifiers.SortByDate

           ' Adjust the column sizes.
           For Each col As ColumnHeader In Me.MyListView.Columns
               col.Width = 100
           Next

           ' Add some items.
           .Items.Add("hello").SubItems.AddRange({"2", "11/11/2000"})
           .Items.Add("yeehaa!").SubItems.AddRange({"1", "9/9/1999"})
           .Items.Add("El3ktr0").SubItems.AddRange({"100", "21/08/2014"})
           .Items.Add("wow").SubItems.AddRange({"10", "11-11-2000"})

           ' Styling things.
           .Dock = DockStyle.Fill
           .View = View.Details
           .FullRowSelect = True
       End With

       With Me ' Styling things.
           .Size = New Size(400, 200)
           .FormBorderStyle = Global.System.Windows.Forms.FormBorderStyle.FixedSingle
           .MaximizeBox = False
           .StartPosition = FormStartPosition.CenterScreen
           .Text = "ListViewColumnSorter TestForm"
       End With

       Me.Controls.Add(Me.MyListView)

   End Sub

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Handles the <see cref="ListView.ColumnClick"/> event of the <see cref="MyListView"/> control.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   Private Sub MyListView_ColumnClick(ByVal sender As Object, ByVal e As ColumnClickEventArgs) _
   Handles MyListView.ColumnClick

       Dim lv As ListView = DirectCast(sender, ListView)

       ' Dinamycaly sets the sort-modifier to sort the column by text, number, or date.
       sorter.SortModifier = DirectCast(lv.Columns(e.Column).Tag, SortModifiers)

       ' Determine whether clicked column is already the column that is being sorted.
       If (e.Column = sorter.ColumnIndex) Then

           ' Reverse the current sort direction for this column.
           If (sorter.Order = SortOrder.Ascending) Then
               sorter.Order = SortOrder.Descending

           Else
               sorter.Order = SortOrder.Ascending

           End If

       Else
           ' Set the column number that is to be sorted, default to ascending.
           sorter.ColumnIndex = e.Column
           sorter.Order = SortOrder.Ascending

       End If ' e.Column

       ' Perform the sort.
       lv.Sort()

   End Sub

End Class





Cita de: phetete en 12 Enero 2016, 21:00 PMhan pasado pvarios años desde el ultimo post.

Está prohibido revivir temas antiguos, si sigues teniendo ese problema con el listview entonces debes formular tus dudas en un nuevo hilo y si quieres puedes hacer referencia a este u otro hilo poniendo la url para mencionar el contenido/cçodigo del post o contestar a cualquiera de las preguntas que te hice...

Tema cerrado.

Saludos!