VB .NET - Procurando itens em um ListView


O controle ListView é um velho conhecido dos tempos do Visual Basic 6.0 e permite exibir uma lista de itens com textos e também com ícones para identificar o tipo de um item. O exemplo mais conhecido é o Windows Explorer que exibe uma lista de arquivos e pastas em uma estrutura de árvore.

Usamos o ListView para apresentar informações e temos que evitar uma grande quantidade de dados procurando otimizar a carga dos dados. Mesmo assim a quantidade de dados pode ser tanta que oferecer o recurso ao usuário para procurar um item específico no controle é muito bem vindo.

Este artigo mostra uma maneira de localizar um item específico em um controle ListView em uma aplicação Windows Forms usando a linguagem VB .NET.

Neste artigo você vai aprender (recordar):

Recursos usados:

Criando a Solução

Abra o Visual Studio 2012 Express for desktop e clique em New Project;

Selecione a linguagem Visual Basic e o template Windows Forms Application informando o nome LocalizandoItens_ListView e clique em OK;

Inclua no formulário form1.vb, a partir da ToolBox, os seguintes controles:

Disponha os controles no formulário conforme o leiaute da figura abaixo:

Definindo a camada de acesso aos dados

Embora não seja objetivo deste artigo eu vou criar uma projeto do Tipo Class Library com o nome DAL onde irei criar uma classe que acessa a tabela Customers do banco de dados Northwind.mdb que esta localizado na pasta g:\dados (na minha máquina local)

No menu FILE clique em Add -> New Project e selecione o template Classs Library informando o nome DAL;

Será criado um novo projeto chamado DAL contendo uma classe Class1.vb;

Antes de prosseguir eu vou incluir a string de conexão com o banco de dados no arquivo de configuração App.Config conforme o código exibido a seguir:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <connectionStrings>
    <clear />
    <add name="strConexaoNorthwind"
     providerName="System.Data.OleDb"
     connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=g:\dados\Northwind.mdb;" />
  </connectionStrings>
</configuration>

Para poder ler e extrair a string de conexão deste arquivo tenho que incluir uma referência no projeto DAL;

Clique com o botão direito do mouse sobre o projeto DAL e selecione Add Reference;

Selecione o item Framework e selecione System.Configuration clicando no botão OK;

Renomeie a classe Class1.vb do projeto DAL para AcessoBD.vb e defina o código abaixo neste arquivo:

Imports System.Data.OleDb
Imports System.Configuration
Public Class AcessoBD
    Dim cnn As OleDbConnection
    Dim dt As DataTable
    Private strConexao As String = ConfigurationManager.ConnectionStrings("strConexaoNorthwind").ToString()
    Public Function GetCustomers(ByVal sql As String) As DataTable
        Try
            dt = New DataTable
            cnn = New OleDbConnection(strConexao)
            cnn.Open()
            Dim da As OleDbDataAdapter = New OleDbDataAdapter(sql, strConexao)
            da.Fill(dt)
            Return dt
        Catch ex As Exception
            Throw ex
        Finally
            If ConnectionState.Open Then
                Try
                    cnn.Close()
                Catch ex As Exception
                    Throw ex
                End Try
            End If
        End Try
    End Function
End Class

Temos aqui a classe AcessoBD e o método GetCustomers que recebe como parâmetro um string que representa o comando SQL a ser executado.

Estou usando a classe ConfigurationManager para acessar e extrair a string de conexão armazenada no arquivo App.Config;

Em seguida acesso a tabela Customers do banco de dados Northwind.mdb e retorno um objeto DataTable com as informações.

Preenchendo o ListView

Agora eu vou voltar ao projeto Windows Forms e abrir o formulário form1.vb no evento Load, onde irei definir o código que preenche o controle ListView.

Defina no início do formulário o código a seguir que cria um a instância da classe AcessoBD;

Dim acessoDados As New DAL.AcessoBD

Agora no evento Load do formulário inclua o código abaixo:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'define um datatable
        Dim dt As New DataTable
        Try
            'chama o método GetCustomers passando a string SQL para consulta
            dt = acessoDados.GetCustomers("Select CustomerID, CompanyName,ContactName, Address, City, Country from Customers")
        Catch ex As Exception
            MsgBox(ex.Message)
            Return
        End Try
        'define o cabeçalho do listview , a largura e o alinhamento
        With lvCustomers
            .Columns.Add("Codigo", 80, HorizontalAlignment.Left)
            .Columns.Add("Empresa", 110, HorizontalAlignment.Left)
            .Columns.Add("Contato", 110, HorizontalAlignment.Left)
            .Columns.Add("Endereco", 90, HorizontalAlignment.Left)
            .Columns.Add("Cidade", 90, HorizontalAlignment.Left)
            .Columns.Add("Pais", 90, HorizontalAlignment.Left)
        End With
        If dt.Rows.Count > 0 Then
            'percorre a tabela pedidos e preenche o listview
            For i = 0 To dt.Rows.Count - 1
                lvCustomers.Items.Add(dt.Rows(i)("CustomerID"))
                lvCustomers.Items(i).SubItems.Add(dt.Rows(i)("CompanyName"))
                lvCustomers.Items(i).SubItems.Add(dt.Rows(i)("ContactName"))
                lvCustomers.Items(i).SubItems.Add(dt.Rows(i)("Address"))
                lvCustomers.Items(i).SubItems.Add(dt.Rows(i)("City"))
                lvCustomers.Items(i).SubItems.Add(dt.Rows(i)("Country"))
            Next
            dt.Clear()
        Else
            MessageBox.Show("Não há dados para esse critério !")
        End If
    End Sub

Neste código eu estou usando o método GetCustomers da classe AcessBD para retornar um DataTable e em seguida estou definindo o cabeçalho do ListView e preenchendo o controle com as informações do DataTable.

Eu estou cometendo um pecado capital neste código criando referências a objetos de acesso a dados na camada de apresentação. O mais correto seria retornar uma lista de objetos ou criar outra camada para tratar o datatable.

Localizando itens no ListView

Agora eu posso definir o código no evento Click do botão de comando Localizar conforme abaixo:

Private Sub btnLocalizar_Click(sender As Object, e As EventArgs) Handles btnLocalizar.Click
        lvCustomers.MultiSelect = False
        lvCustomers.FullRowSelect = True

        'chama a função para encontrar o item
        Dim verificaItem As Integer = EncontraItem(lvCustomers, TextBox1.Text)
        'se não retornou -1 então exibe a linha no listview
        If verificaItem <> -1 Then
            lvCustomers.Items(verificaItem ).Selected = True
            'define o foco no controle Listview
            lvCustomers.Focus()
            'assegura que se o item estiver em uma parte não visível ele será exibido 
            lvCustomers.SelectedItems(0).EnsureVisible()
        Else
            lblmsg.Text = "Não localizado !"
        End If
    End Sub

Este código chama o método EncontraItem() passando o nome do controle ListView e o critério informando no controle TextBox1;

Eu configurei MultiSelect como False para que somente a primeira ocorrência seja retornada.

A função EncontraItem é chamada e, em seguida, o bloco If-Then-Else lida com o resultado retornado. Se o resultado não for -1, então um valor foi encontrado. Esse número de linha é então selecionado.

Como a última ação do usuário foi clicar no botão, ele ainda terá foco, então na seleção lista não será destacado (e o usuário pensaria que a pesquisa falhou). Ajustando o foco para o listview resolvemos este problema.Ex:
lvCustomers.Focus()

Se o valor devolvido é -1, então é apresentada uma mensagem informando o usuário que a busca falhou.

Muitas vezes a linha que está sendo procurada não será uma das linhas que está visível. Então, se for encontrada uma correspondência e a linha for selecionada o usuário provavelmente não vai saber pois a linha não esta visível. Para contornar este problema usamos o seguinte comando:

lvCustomers.SelectedItems(0).EnsureVisible()

O código do método EncontraItem() é visto a seguir:

 Private Function EncontraItem(ByVal LV As ListView, ByVal TextToFind As String) As Integer
        ' percorre os ListViewItems.
        For i As Integer = 0 To LV.Items.Count - 1
            If Trim(LV.Items(i).Text) = Trim(TextToFind) Then
                ' Se encontrar retorna o numero da linha
                Return (i)
            End If
            For subitem As Integer = 0 To LV.Items(i).SubItems.Count - 1
                If Trim(LV.Items(i).SubItems(subitem).Text) = Trim(TextToFind) Then
                    ' se encontrar retorna o numero da linha
                    Return (i)
                End If
            Next
        Next
        ' Se não encontrar nada retorna -1.
        Return -1
    End Function

A primeira parte do ciclo For-Next verifica se o critério de pesquisa existe na primeira coluna da exibição do listview. Se isso acontecer, então o número de índice da linha é retornado.

Se não for encontrado na primeira coluna, as colunas restantes colunas desta linha são verificadas. Novamente, se for encontrada uma correspondência, o número de índice da linha é retornado.

O ciclo continua até que seja encontrada uma correspondência ou o fim da exibição do controle for atingido. Neste caso um valor -1 é retornado.

Executando o projeto, informando um critério para busca e clicando o botão Localizar temos o resultado abaixo:

Note que você pode usar como critério qualquer valor de qualquer coluna, mas ele tem que ser exatamente igual ao exibido para ser localizado.

Pegue o projeto completo aqui: LocalizandoItens_ListView.zip

Joã 11:25 Declarou-lhe Jesus: Eu sou a ressurreição e a vida; quem crê em mim, ainda que morra, viverá;

Joã 11:26 e todo aquele que vive, e crê em mim, jamais morrerá. Crês isto?

Referências:


José Carlos Macoratti