VB.NET - Uma introdução básica as controles de lista


Neste artigo vou falar um pouco sobre os controles de lista : ListBox , CheckedListBox e Combobox. Todos estes controles derivam da classe base ListControl . Esta classe é uma classe abstrata (MustInherit no VB.NET) e não pode ser instanciada , mas as classes concretas citadas e delas derivadas podem ser instanciadas e usadas normalmente conforme veremos a seguir.

A classe ListControl fornece muito das funcionalidades comuns entre estes três controles. A tabela abaixo lista as cinco propriedades nativas da classe ListControl que são herdadas pelas classes derivadas.

Propriedades da classe ListControl

Propriedade Tipo Descrição
DataSource Object Read/write. Define a fonte de dados para o controle de lista
DisplayMember String Read/write. Define a propriedade da fonte de dados a ser exibida no controle.
SelectedValue Object Read/write. Contém o valor do item selecionado atual definido pela propriedade ValueMember. Se ValueMember nao estiver definido retorna : object.ToString( ).
ValueMember String Read/write. Define a propriedade da fonte de dados retornada pela propriedade SelectedValue.  Pode ser anulada atribuindo uma string vazia ou uma referencia Null.
SelectedIndex Integer Read/write. O indice baseado no valor inicial zero do item atualmente selecionado. O valor -1 indica que nenhum item esta selecionado.

Estas propriedades incluem a propriedade DataSource e muitas propriedades úteis . Nas tabelas a seguir temos as propriedades/métodos que não são membros da classe ListControl mas são membros para todos os três controles derivados desta classe. Elas incluem propriedades para definir a aparência do controle e propriedades relacionadas com a coleção de itens dos controles.

Propriedades comum a todos os controles de lista

Propriedade tipo Descrição
IntegralHeight Boolean Read/write. Se True (o padrão) o controle se redimensiona para evitar mostrar itens parciais.
DrawMode DrawMode Read/write. Define o modo de desenho para o controle. Os valores válidos são:
- DrawMode.Normal (padrão), DrawMode.OwnerDrawFixed, and DrawMode.OwnerDrawVariable.
ItemHeight Integer Read/write. Altura de um item no controle em pixels.
Items ObjectCollection Read-only. Coleção de itens no controle.
PreferredHeight Integer Read-only. A altura de todos os itens no controle em pixels. Esta é a altura que o controle precisa para exibir todos os itens se efetuar uma rolagem vertical.
SelectedItem Object Read/write. O item atualmente selecionado no controle.
Sorted Boolean Read/write. Se false (o padrão), os itens no controle não estão ordenados e novos itens são incluídos no final da lista. De outra forma eles são ordenados em ordem crescente , em ordem alfabética , e case-insensitive. Se True ,  o indice de um item especifico pode ser alterado conforme novos itens forem sendo incluídos.
Text String Read/write. O texto associado ao item atualmente selecionado.

Métodos comum a todos os controles de lista

Método Descrição
BeginUpdate Evita o redesenho do controle enquanto itens são incluídos na coleção de itens.
EndUpdate Recupera o desenho do controle depois que BeginUpdate foi invocado.
FindString Overloaded. Retorna um índice baseado em zero do primeiro item na coleção de itens que inicia com uma string definida.
FindStringExact Overloaded. Retorna um índice baseado em zero do primeiro item da coleção deitens que coincide exatamente com a string definida

Preenchendo um Controle de Lista

Vamos então ao que interessa.

Há duas maneiras de preencher os controles de lista:

  1. Usando a propriedade Items do controle
  2. Vinculando o controle a uma fonte de dados.

A propriedade Items representa uma coleção de objetos contidos em uma lista ; como toda a coleção de objetos , ela implementa as interfaces IList, ICollection e IEnumerable e fornece métodos para inclui , excluir e manipular a coleção. Abaixo temos os métodos mais usados para isto:

Métodos mais comuns usados em ObjectCollection

Método Descrição
Add Inclui um novo objeto a coleção de itens atual. Retorna um índice baseado em zero de um item na coleção.
AddRange Inclui um array de objetos na coleção
Clear Remove todos os itens de uma coleção.
Contains Retorna true se o objeto especificado é encontrado na coleção.
CopyTo Copie a coleção completa para um objeto array definido iniciando no índice especificado.
IndexOf Retorna um índice baseado em zero de um objeto definido dentro da coleção. Se o objeto não for encontrado retorna -1.
Insert Insere um objeto em uma coleção de um índice definido. Se o If the Sorted property is true, the index is ignored.
Remove Remove o objeto especificado da coleção.Todos os objetos se movem uma posição.
RemoveAt Remove o objeto da coleção na posição definida pelo índice.Todos os objetos se movem uma posição.

Se olharmos as tabelas veremos que a coleção Items é do tipo ObjectCollection , acima temos os métodos mais usados de ObjectCollection.

Note porem que não existe uma classe ObjectCollection. Cada um dos controles derivados da classe ListControl possui a sua própria classe ObjectCollection: ObjectCollection, CheckedListBox.ObjectCollection, e ComboBox.ObjectCollection. Porém todas as 3 classes contém essencialmente os mesmos métodos com algumas exceções que veremos a seguir.

Além disto a classe ObjectCollection possui a propriedade de somente-leitura Count que retorna o número de itens na coleção e a propriedade Item de leitura/escrita que é um indexador dentro da coleção. Vamos falar delas logo , logo...

Quando você usa o IDE do VS.NET , strings podem ser incluídas em uma coleção de itens em tempo de desenho usando o Editor de Strings para Coleções. O acesso é feito quando você clica no botão com três pontos(...) perto da propriedade Items na janela Property. A seguir basta digitar a relação de strings e clicar no botão OK.

O editor utiliza o método AddRange para incluir strings para a coleção items. O código que faz isto reside no método InitializeComponente gerado pelo VS.NET.

A segunda maneira de preencher um controle de lista é vincular o controle a uma fonte de dados usando a propriedade DataSource. A fonte de dados pode ser qualquer objeto que implementa a interface IList. Esta interface representa uma coleção de objetos que são individualmente acessados via index:

A propriedade DataSource vincula os dados na fonte de dados do controle. Geralmente um DataSource possui um ou mais membros , tais como DataColumns em um DataTable.

Vamos supor que você tem um banco de dados que contém todos os estados do país. Suponha que esta tabela possua duas colunas : UF e Nome do estado. Você gostaria de exibir o nome do estado no controle de lista , mas gostaria de passar a abreviação para o programa que esta acessando o controle. O código que faz este serviço é o seguinte :

' Cria uma conexão e um comando
 Dim conStr As String = "Provider=Microsoft.JET.OLEDB.4.0;data source=d:\teste\northwind.mdb"
 Dim sqlStr As String = "SELECT * from estados"
 ' Cria um objeto connection
 Dim conn As OleDbConnection = New OleDbConnection(conStr)
 ' Cria o objeto dataadapter
 Dim da As OleDbDataAdapter = New OleDbDataAdapter(sqlStr, conn)
 ' Cria o dataset e preenche com os dados do datadapter
 Dim ds As DataSet = New DataSet
 da.Fill(ds, "estados")
 ' vincula o DefaultView do dataset ao controle
 Dim dv As DataView = ds.Tables("estados").DefaultView
 lb.DataSource = dv
 lb.DisplayMember = "NomeEstado"
 lb.ValueMember = "UF"
 lb.SelectedIndex = 0
conn.Close()

O código acima apenas faz a conexão com a fonte de dados e exibe os dados em um controle ListBox. Para extrair o valor do membro do controle para processamento podemos usar o evento SelectedValueChanged :

  Dim strEstado As String
  If lb.SelectedIndex <> -1 Then
     strEstado = CType(lb.SelectedItem, DataRowView)("NomeEstado").ToString()
     lblselecao.Text = " Selecionou : " & strEstado
  End If		

Você dever realizar um cast (conversao forçada) a propriedade SelectedValue para um tipo correto (neste caso um string) pois a propriedade é herdada de um tipo objeto, como cada item da coleção Items.

Abaixo temos o código onde um controle ListBox é preenchido a partir de um tabela sem usar a propriedade DataSource . Ao invés disto fazemos uma iteração na tabela e o método add da classe Items é chamado para cada registro.

   Dim connectionString As String
  connectionString = "Provider=Microsoft.JET.OLEDB.4.0;data source=d:\teste\northwind.mdb"
   Dim commandString As String
    commandString = "Select * from estados"
    Dim da As New OleDbDataAdapter(commandString, connectionString)
    Dim ds As New DataSet
    da.Fill(ds, "estados")
    Dim dt As DataTable
    dt = ds.Tables(0)
    Dim linha As DataRow
    For Each linha In dt.Rows
        lb.Items.Add(linha("UF") & ": " & linha("NomeEstado"))
    Next

Esta técnica oferece dois benefícios sobre o databinding. O Primeiro é que muito conveniente exibir a concatenação de um ou mais campos como um string texto. O segundo deriva do fato de que se a propriedade DataSource é usada, então a coleção Items da lista não pode ser modificada.

Esta técnica evita que seja possível modificar os itens da coleção. Porém você perde a habilidade de exibir um valor na lista representado pela propriedade DisplayMember e retornar um valor diferente para processamento posterior representado pela propriedade ValueMember.

Você pode usar a propriedade DataSource e a vinculação de dados e alem disto preservar o uso das propriedades DisplayMember e ValueMember enquanto ainda estiver concatenando os campos e o texto. Vejamos um exemplo onde iremos retornar o nome do Estado e a UF da tabela estados de um banco de dados northwind.mdb.(Criei esta tabela no banco de dados para o artigo)

Dim connectionString As String
        connectionString = "Provider=Microsoft.JET.OLEDB.4.0;data source=d:\teste\northwind.mdb"
        Dim commandString As String
        commandString = "Select * from estados"
        Dim da As New OleDbDataAdapter(commandString, connectionString)
        Dim ds As New DataSet
        da.Fill(ds, "estados")
        Dim dt As DataTable
        dt = ds.Tables(0)

       ' vincula o controle a tabela
       lb.DataSource= dt;
       lb.DisplayMember = "NomeEstado";
       lb.ValueMember = "UF";

A consulta SQL contida na string de comando retorna a coluna código mais a concatenação do primeiro nome com o sobrenome separados por uma virgula que formam o campo nome.

Logo depois de definir a propriedade DataSource , a propriedade DisplayMember é definida para membro NomeEstado e a propriedade ValueMember é definida para o membro UF. O resultado obtido pode ser exibido em um ListBox.

O código do evento Click do botão de comando é exibido abaixo. Se houver uma seleção no ListBox, sua propriedade SelectionMode é definida para o valor One:

    If lb.SelectedIndex <> -1 Then
            Dim s As String = CType(lb.SelectedItem, DataRowView)("NomeEstado").ToString()
            MessageBox.Show("Valor :  " + lb.SelectedValue.ToString() + vbCrLf + "Exibir :  " + s)
        Else
            MessageBox.Show("Nada selecionado")
      End If

O resultado da caixa de mensagem é mostrado abaixo:

No exemplo a propriedade SelectedValue e o método ToString mostra o membro especificado pela propriedade ValueMember porém exibe o membro definido pela propriedade DisplayMember . Você poderia substituir o argumento do MessageBox com o seguinte codigo:

MessageBox.Show("Valor : " + lb.SelectedValue.ToString(  ) + vbCrLf +  "Exibir :" + lb.SelectedItem.ToString(  ))

Ao fazer isto o resultado será o exibido abaixo. Este resultado evidencia que os itens na coleção Items são objetos e neste caso objetos do tipo DataRowView.

Você poderia esperar que os itens na coleção Items deste exemplo fossem objetos DataRow , desde que a propriedade DataSource é um DataTable porém sempre que um dado é exibido em um formulário Windows Forms ele é exibido como um objeto DataRowView.

Por causa disto o código realiza um cast no SelectedItem para um objeto DataRowView e chama o método ToString para convertê-lo para uma string para exibir no MessageBox.

Isto funciona bem , exceto por dois efeitos colaterais : isto requer que você informe o nome do membro e ,  se o ListBox fosse definido como sendo multiselected ele irá exibir somente o primeiro elemento selecionado.

Para tratar um multi-seleção no controle devemos usar o seguinte código:

        Dim strMsg As String = ""
 If lb.SelectedIndex <> -1 Then
    Dim item As Object
    For Each item In lb.SelectedItems
       Dim s1 As String = CType(item, DataRowView)(lb.ValueMember).ToString()
       Dim s2 As String = CType(item, DataRowView)(lb.DisplayMember).ToString()
       trMsg += "Valor :  " + s1 + vbCrLf + "Exibir :  " + s2 + vbCrLf + vbCrLf
   Next
       MessageBox.Show(strMsg)
Else
      MessageBox.Show("Nada selecionado")
End If

A solução do código acima realiza uma iteração através da coleção SelectedItems, construindo uma string para exibir no controle MessageBox. O nome dos membros não estão incluídos fixos no código , mas as propriedades ValueMember e DisplayMember são usadas diretamente para inspecionar o interior dos objetos DataRowVIew. O resultado será os itens selecionados exibidos.

Os exemplos acima mostram diferentes formas de retornar o texto associado com um item na coleção Items usando as propriedades SeletecdItem, SelectedItems e SelectedValue. A classe ListControl fornece também o método GetItemText que simplifica esta tarefa pois toma um item objeto como um argumento e retorna uma string.

Usando o método GetItem , você pode reescrever e consolidar os exemplos acima :

 Dim strMsg As String = ""
        If lb.SelectedIndex <> -1 Then
            '  para seleção simples no listbox
            MessageBox.Show("Item Texto :  " + lb.GetItemText(lb.SelectedItem))
            '  para seleção múltiplas no listbox
            Dim item As Object
            For Each item In lb.SelectedItems
                Dim s1 As String = CType(item, DataRowView)(lb.ValueMember).ToString()
                Dim s2 As String = CType(item, DataRowView)(lb.DisplayMember).ToString()
                Dim s3 As String = lb.GetItemText(item)
                strMsg += "Valor: " + s1 + vbCrLf + "Exibe : " + s2 + vbCrLf + "Item texto : " + s3 + vbCrLf + vbCrLf
            Next
            MessageBox.Show(strMsg)
        Else
            MessageBox.Show("Nada Selecinado")
        End If

 

Finalmente existe um outra forma ainda simples de retornar e exibir um item. A propriedade Text da classe derivada de LisControl sobre-escreve a propriedade Text do controle Base.

A propriedade Text dos controles ListBox, CheckedListBox, and ComboBox reflete não a string associada com o próprio controle , mas o item atualmente selecionado. . No caso de controles que suportam multi selecao (ListBox and CheckedListBox) ele corresponde ao texto do primeiro item selecionado.

Para exibir o valor de um simples item selecionado usamos o seguinte código:

MessageBox.Show("Text:  " + lb.Text)

Creio que deu para você ter uma visão panorâmica dos controles de lista e obter os conceitos básicos para saber usá-los em suas aplicações .NET.

Arriverdeci !!!

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti