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):
Armazenar e ler uma string de conexão de um arquivo app.config
Acessar um banco de dados Access usando ADO .NET
Criar uma classe que retorna um objeto DataTable
Preencher um controle ListView com dados de uma tabela
Localizar itens em um ListView selecionando a linha com o item encontrado
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.AcessoBDAgora 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:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#