sexta-feira, 18 de abril de 2008

Ordenar Listview

Um problema que muitas vezes enfrentamos quando estamos trabalhando com Grid e Listview é a classificação, pois a mesma nunca se comporta da forma esperada quando trabalhamos com datas ou números. A minha idéia neste artigo é mostrar como fazer isso utilizando uma classe personalizada. Pois bem, o primeiro passo a ser seguido é abrir o Visual Studio - no meu caso estou utilizando o VB 2008 Express - e criar um novo projeto. Vamos nomear este projeto como OrdenarListview.vb.


Feito isso clique em Project Add Class...
Iremos renomear esta classe para CompararItensListview.vb.


Vamos ao código.

A primeira coisa a ser feita é definir o que você vai precisar ordenar. Neste exemplo irei demonstrar como ordenar Data, números e Texto. Iremos agora declarar nossas variáveis e criar o codigo para receber as mesmas sempre que a nossa classe for chamada. Usaremos a palavra Implements para implementar a Interface IComparer. Interfaces são classes que possuem apenas metodos e não possuem implementação, ou seja, a mesma não possui código apenas chamadas de metodos. A classe que herdar a Inteface terá que implementar o seu método e seguir o modelo de sua chamada.

'IMPLEMENTA A Interface IComparer

Implements IComparer
VERIFICAR ORDEM

Private Coluna As Integer
'VARIAVEL QUE VAI RECEBER O TIPO DE ORDEM
'QUE NOSSO LISTVIEW VAI SEGUIR
“Descending ou Ascending”
Private
OrdemSort As SortOrder

Agora definiremos como iremos receber os valores Coluna e OrdemSort. Neste exemplo, os mesmos serão recebidos junto à chamada da Classe, ou seja, no procedimento Public Sub New.

Public
Sub New(ByVal ColunaClique As Integer,ByVal OrdemSort As SortOrder)
Coluna = ColunaClique
Me.OrdemSort = OrdemSort
End Sub

Nosso código deve ficar assim:

Class CompararItensListview
Implements IComparer
Private Coluna As Integer
Private OrdemSort As SortOrder

Public Sub New(ByVal ColunaClique As _
Integer, ByVal OrdemSort As SortOrder)
Coluna = ColunaClique
Me.OrdemSort = OrdemSort
End Sub
End Class

O que temos que fazer agora é a função que vai organizar o Listview. O método que iremos implementar da Classe IComparer é a função compare que possui dois parâmetros: o X que representa o item que vai ser comparado; e o Y, o item anterior a este na ordem conforme exemplo abaixo:

Public Function Compare(ByVal x As Object,ByVal y As
Object) As Integer _
Implements System.Collections.IComparer.Compare
End Function

Veremos agora como fazer a comparação entre os valores do Listview para estabelecer a ordem dos mesmos. O que faremos agora é uma verificação do tipo de dados da coluna selecionada e sua validação conforme este. Por exemplo, suponhamos que na sua coluna você possui datas. Para fazer esta verificação usaremos a função DateTime.TryParse que é semelhante ao IsDate do antigo VB6.

'PARA UTILIZAR A FUNÇÃO DateTime.TryParse TEMOS QUE DEFINIR UMA DATA VÁLIDA CONFORME EXEMPLO ABAIXO
Dim DataValida As DateTime
If DateTime.TryParse(CType(x, _
ListViewItem).SubItems(Coluna).Text, DataValida) Then

Perceba que na função acima utilizamos a função CType, função esta que serve para converter uma variável de um tipo para outro CType(x, ListViewItem).SubItems(Coluna).Text. Neste caso, estamos convertendo um tipo Objeto para um tipo ListViewItem e capturando assim o seu valor. Utilizaremos também a função Double.TryParse para verificar se o valor é numérico. Bem, a partir daqui não vou me ater muito, pois é apenas uma lógica de comparação para verificar a ordem que cada um vai se posicionar em relação ao item superior, conforme tabela abaixo.

0 – Igual ao item anterior fica onde esta
-1 – É menor sobe para a posição do item anterior
1 – Vai uma posição para frente

Para fazer a comparação entre os valores usaremos as funções.
DateTime.Compare – Comparar duas dadas
Convert.ToDouble – transforma em valor para comparação
String.Compare -
compara duas strings
O código da nossa Classe deve ficar semelhante a este:


Class CompararItensListview

    Implements IComparer

    Private Coluna As Integer

    Private OrdemSort As SortOrder

 

    Public Sub New(ByVal ColunaClique As Integer, ByVal _

    OrdemSort As SortOrder)

 

        Coluna = ColunaClique

        Me.OrdemSort = OrdemSort

 

    End Sub

 

    Public Function Compare(ByVal x As Object, ByVal y As Object) _

    As Integer Implements System.Collections.IComparer.Compare

 

        Dim OrdemItem As Integer

        Dim DataValida As DateTime

        Dim NumeroValido As Double

 

        If DateTime.TryParse(CType(x, ListViewItem).SubItems(Coluna).Text, _

        DataValida) Then

 

            If DateTime.TryParse(CType(y, ListViewItem).SubItems(Coluna).Text, _

            DataValida) Then

 

                OrdemItem = DateTime.Compare(CType(x, ListViewItem).SubItems _

                (Coluna).Text, CType(y, ListViewItem).SubItems(Coluna).Text)

 

                MsgBox(CType(x, ListViewItem).SubItems(Coluna).Text)

                MsgBox(CType(y, ListViewItem).SubItems(Coluna).Text)

 

            Else

                OrdemItem = -1

            End If

 

        ElseIf Double.TryParse(CType(x, ListViewItem).SubItems(Coluna) _

            .Text, NumeroValido) Then

 

            If Double.TryParse(CType(y, ListViewItem).SubItems(Coluna).Text _

            , NumeroValido) Then

 

                If Convert.ToDouble(CType(x, ListViewItem).SubItems(Coluna).Text) _

                = Convert.ToDouble(CType(y, ListViewItem).SubItems(Coluna).Text) Then

 

                    OrdemItem = 0

                ElseIf Convert.ToDouble(CType(x, ListViewItem).SubItems _

                (Coluna).Text) > Convert.ToDouble(CType(y, ListViewItem). _

                SubItems(Coluna).Text) Then

 

                    OrdemItem = 1

                Else

                    OrdemItem = -1

                End If

            Else

                OrdemItem = -1

            End If

        Else

            If CType(x, ListViewItem).SubItems(Coluna).Text.Trim = "" Then

                OrdemItem = 1

            Else

                OrdemItem = String.Compare(CType(x, ListViewItem). _

                SubItems(Coluna).Text, CType(y, ListViewItem).SubItems _

                (Coluna).Text)

 

            End If

        End If

 

        'INVERTE O RETORNO EM CASO DE ORDEM DECRESCENTE

        If OrdemSort = SortOrder.Descending Then

            OrdemItem *= -1

        End If

 

        Return OrdemItem

 

    End Function

End Class

Vamos ao codigo do formulário insira um Listview no Form criado e coloque o código abaixo:

Dim ColunaSelecionada As Integer = -1
Private Sub ListView1_ColumnClick(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ColumnClickEventArgs)
Handles ListView1.ColumnClick


'VERIFICA SE CLIQUE É NA MESMA COLUNA
'SE FOR ORDENA DECRESCENTE
If e.Column <> ColunaSelecionada Then
ColunaSelecionada = e.Column
ListView1.Sorting = SortOrder.Ascending
Else
If ListView1.Sorting = SortOrder.Ascending Then
ListView1.Sorting = SortOrder.Descending
Else
ListView1.Sorting = SortOrder.Ascending
End If
End If
'CHAMA METODO QUE IRA ORDENAR O LISTIVEW
ListView1.ListViewItemSorter = New _
CompararItensListview(e.Column, ListView1.Sorting)

End Sub


Para testar insira alguns dados no seu Listview varie as colunas com datas, numeros e texto para ver se funciona abraços e até o proximo.

Baixe o projeto completo escrito no Visual Studio 2008 no link abaixo:

Ordenar Listview

4 comentários:

Anônimo disse...
Este comentário foi removido pelo autor.
Anônimo disse...

Olá Amigo, show de bola essa sua classe de ordenação, comecei a usá-la, porém obtive um erro inesperado quando tentava repreencher o listview, um erro com a seguinte mensagem: "InvalidArgument=Value of '1' is not valid for 'index'. Parameter name: index". Aos poucos descobri que o erro era devido ao fato de a sua classe mudar a ordem dos índices do listview e consegui resolver o problema e gostaria de compartolhar a solução com os amigos desenvolvedores:
após clicar nas colunas os índices do listview são mudados, postanto sempre que formos repreenche-lo (usando um "select" após cadastro ou fazendo uma consulta) temos que colocar a ordenação na forma original. Para isso usei o seguinte código que resolveu de cara o meu problema:
"ListView1.Sorting = SortOrder.None"

deixo meu email para qualquer dúvida:
helcio.carmo.ribeiro@hotmail.com

Amigo, obrigado por tudo!

Anônimo disse...

Topico muito bom cara, funcionou perfeitamente na minha aplicação,

Valeu

Anônimo disse...

Preciso de uma ajuda para criar um listview vindo do banco de dados.me manda um emial para eu entrar em contator. mbarroscribeiro@hotmail.com