Data Binding no Visual Basic .NET

Como obter o melhor do data binding nos aplicativos em Visual Basic .NET.

Clique aqui para ler todos os artigos desta edição

 

Data Binding no Visual Basic .NET

por Ken Spencer

 

Como posso obter o melhor do data binding nos aplicativos em Visual Basic® .NET?

 

Primeiro, vamos tratar um pouco da arquitetura. A Figura 1 mostra uma visão geral de uma maneira comum de usar data binding com uma fonte de dados (data source). Os dados são extraídos da fonte por meio de uma biblioteca de dados e colocados em um DataSet não tipado. O DataSet não tipado é usado para permitir o uso de uma biblioteca de acesso a dados genéricos. Isso lhe permite chamar uma única função que retorne qualquer DataSet. Você pode pegar esse DataSet não tipado e mesclá-lo a um DataSet tipado, usando duas tabelas correspondentes. Por fim, você pode vincular controles ao DataSet tipado.

 

Figura 1. Vinculando

 

Você deve estar se perguntando por que eu não estabeleci um vínculo diretamente com o DataSet não tipado. O DataSet tipado oferece recursos como early binding de campos. Isso permite que você use .CustomerName como um membro do DataSet ao invés de fazer referência à coleção Items com o valor "CustomerName," o que faz com que o processo de acesso a dados seja muito mais objetivo. O early binding também fornece suporte automático para data binding (vinculação de dados) em design-time. Com isso, você pode selecionar campos no editor e até mesmo acessar propriedades personalizadas em determinados controles.

Agora, vamos examinar o data binding. Observe a função a seguir que extrai os dados do SQL Server™ e retorna um DataSet:

 

Function RetrieveCustomerContacts() As DataSet

  Dim ds As DataSet

  Try

ds = RunSQLWithDataSet("Select CustomerID,  " & _

"CompanyName,ContactName,NoOfCustomerVisits " & _

"from customers", ConnectionString, "Customers")

  Catch ex As Exception

ds = Nothing

  End Try

  Return ds

End Function

 

Em seguida, considere um DataSet tipado chamado Customers com os campos a seguir, que adicionei ao projeto como dsCustomers.xsd (parte do XSD foi omitida para poupar espaço):

 

 

 

 

minOccurs="0" />

 

minOccurs="0" />

 

No Visual Studio® .NET, você pode associar esses dados a um DataSet arrastando um componente DataSet da toolbox e soltando-o em seu formulário e, em seguida, associando-o ao DataSet tipado Customers. Agora você pode usar a janela de propriedades de um controle DataGrid para selecionar o novo DataSet como o DataSource e a tabela Customers como o DataMember. Complete o processo adicionando o seguinte código ao evento Load do formulário:

 

Dim ds As DataSet

Dim oClass As New SomeComponent.Class1

Try

  ds = oClass.RetrieveCustomerContacts

  dsCustomers.Merge(ds)

Catch ex As Exception

  sbarmain.Text = ex.Message

End Try

 

Esse código chama a classe RetrieveCustomerContacts para recuperar o DataSet e mesclá-lo (Merge) ao DataSet tipado em seu formulário. É claro, você também pode executar esse tipo de data binding com outros controles, como listbox e combobox. Existe outro ótimo recurso a ser considerado. O mesmo código recém-mostrado pode ser usado para fazer com que o DataSet tipado seja carregado em sua combobox. Em seguida, você pode capturar o valor selecionado usando apenas uma única linha de código no evento SelectedIndexChanged:

 

txtSelectedID.Text = ComboBox1.SelectedValue

 

Se você estiver vinculando o controle txtSelectedID a uma classe ou estrutura cuja propriedade chama-se CustomerID, você poderá usar o seguinte código ao invés de colocar os dados diretamente no controle:

 

CustomerContacts.CustomerID = ComboBox1.SelectedValue

 

Isso atualizaria o campo CustomerID da classe e o data binding atualizaria o valor mostrado no textbox. Vamos ver uma outra maneira do data binding tornar seu aplicativo orientado a dados mais amigável ao usuário. Suponha, por exemplo, que você tem um aplicativo que precise mostrar os dados em três textboxes a partir de um DataSet ou de qualquer outro objeto vinculável. O CurrencyManager pode ser usado para navegar nesses controles e fornecer outros recursos. A interface para este formulário é mostrada na Figura 2.

 

Figura 2. Interface do Currency Manager

 

Este formulário permite que você percorra um DataSet e altere os dados à medida que navegar. Cada vez que você altera um valor e passa para outro registro, a alteração é persistida no DataSet. Essa maneira é muito semelhante à maneira como o data binding funcionava no Visual Basic 6.0 e nas versões anteriores. Ela é mais flexível agora com o VB .NET porque você tem acesso aos recursos low-level do data binding.

Veja o código da Listagem 1 que apesar de ser bastante objetivo, requer algumas explicações. Omitirei algumas partes, como a classe que extrai os dados do SQL Server e os retorna, mas o código completo está disponível para download.

 

Listagem 1 Elementos de Código-Chave para Data Binding

Private WithEvents thisCurrencyManager As CurrencyManager

 

'Definição das variáveis entram aqui

'Form Load cria o dataset e o mescla ao

'CustomerInfo1 e vincula os controles chamando

'BindControls

 

Private Sub BindControls(ByVal thisDataTable As DataTable)

  'Vincula um TextBox a coluna DataTable no DataSet

  txtCompany.DataBindings.Add("Text", _

thisDataTable, "CompanyName")

  txtName.DataBindings.Add("Text", _

thisDataTable, "ContactName")

  txtCity.DataBindings.Add("Text", _

thisDataTable, "City")

  txtCustomerID.DataBindings.Add("Text", _

thisDataTable, "CustomerID")

 

  'Especifica o CurrencyManager para o DataTable

  thisCurrencyManager = _

CType(Me.BindingContext(thisDataTable), _

CurrencyManager)

  'Define a posição inicial do controle

  thisCurrencyManager.Position = 0

End Sub

 

Private Sub MoveNext(ByVal thisCurrencyManager As CurrencyManager)

  If thisCurrencyManager.Position = _

thisCurrencyManager.Count - 1 Then

MessageBox.Show( _

"Você está no final dos registros")

  Else

thisCurrencyManager.Position += 1

CheckChanges()

  End If

End Sub

 

Private Sub MoveFirst(ByVal thisCurrencyManager As CurrencyManager)

  thisCurrencyManager.Position = 0

  CheckChanges()

End Sub

 

Private Sub MovePrevious(ByVal thisCurrencyManager As CurrencyManager)

  If thisCurrencyManager.Position = 0 Then

MessageBox.Show( _

"Você está no início dos registros.")

  Else

thisCurrencyManager.Position -= 1

CheckChanges()

  End If

End Sub

 

Private Sub MoveLast(ByVal thisCurrencyManager As CurrencyManager)

  thisCurrencyManager.Position = _

  thisCurrencyManager.Count - 1

  CheckChanges()

End Sub

 

'Eventos de clique no botão entram aqui

 

Private Sub txtName_TextChanged( _

  ByVal sender As System.Object, _

  ByVal e As System.EventArgs) _

  Handles txtName.TextChanged

  'Abandona, caso não exista

  If IsNothing(thisCurrencyManager) Then

Exit Sub

  End If

 

  StartEditMode()

End Sub

 

Private Sub txtCompany_TextChanged( _

  ByVal sender As System.Object, _

  ByVal e As System.EventArgs) _

  Handles txtCompany.TextChanged

  StartEditMode()

End Sub

 

Private Sub txtCity_TextChanged( _

  ByVal sender As System.Object, _

  ByVal e As System.EventArgs) _

  Handles txtCity.TextChanged

  StartEditMode()

End Sub

 

Sub StartEditMode()

  cmdSaveChanges.Enabled = True

End Sub

 

Sub EndEditMode()

Me.BindingContext(CustomerInfo1.Customers).EndCurrentEdit()

End Sub

 

Private Sub thisCurrencyManager_PositionChanged( _

  ByVal sender As Object, _

  ByVal e As System.EventArgs) _

  Handles thisCurrencyManager.PositionChanged

  cmdSaveChanges.Enabled = False

End Sub

 

Private Sub cmdNew_Click( _

  ByVal sender As System.Object, _

  ByVal e As System.EventArgs) _

  Handles cmdNew.Click

  thisCurrencyManager.AddNew()

End Sub

 

'Outros subs entram aqui

 

A primeira linha de código da Listagem 1 define o CurrencyManager para meu aplicativo. Ele é definido com WithEvents para permitir que eu use os eventos se precisar deles:

 

Private WithEvents thisCurrencyManager As CurrencyManager

 

O código no evento frmBinder2_Load é relativamente padrão. Ele chama um método que retorna um DataSet e, em seguida, mescla esse DataSet a um DataSet tipado. A tabela Customers do DataSet tipado é passada para o método BindControls que executa a vinculação (binding).

O método BindControls é onde você vê o CurrencyManager em ação. As primeiras linhas vinculam quatro campos do DataSet à propriedade Text dos controles:

 

txtCompany.DataBindings.Add("Text", _

  thisDataTable, "CompanyName")

txtName.DataBindings.Add("Text", _

  thisDataTable, "ContactName")

txtCity.DataBindings.Add("Text", _

  thisDataTable, "City")

txtCustomerID.DataBindings.Add("Text", _

  thisDataTable, "CustomerID")

 

As duas últimas linhas de BindControls na prática usam o CurrencyManager. A primeira define o CurrencyManager para o BindingContext da fonte de dados (neste caso, a DataTable):

 

thisCurrencyManager = _

  CType(Me.BindingContext(thisDataTable), _

  CurrencyManager)

 

A linha seguinte define a posição inicial como 0 (o primeiro registro):

 

thisCurrencyManager.Position = 0

 

Agora, eu posso prosseguir com o CurrencyManager. O método Move usa o CurrencyManager para trocar todas as posições dentro do DataSet. Por exemplo, o método MoveNext avançará o ponteiro do elemento atual na fonte de dados desde que a posição atual não seja o último elemento:

 

If thisCurrencyManager.Position = _

   thisCurrencyManager.Count - 1 Then

   MessageBox.Show( _

"Você está no final dos registros")

Else

   thisCurrencyManager.Position += 1

   CheckChanges()

End If

 

O método CheckChanges é chamado sempre que a posição é alterada para determinar se o DataSet sofreu alguma alteração. Enquanto criava este aplicativo de exemplo, lutei durante horas com um pequeno problema. Eu conseguia navegar nos registros e alterar os dados sem grandes problemas, desde que passasse para outra linha nos dados. No entanto, quando eu usava o botão Save para salvar as alterações, o método GetChanges não indicava apropriadamente a ocorrência das alterações. Verifiquei o código de acordo com a documentação do MSDN Library e com os exemplos on-line e, após uma alguma investigação, compreendi o que se passava. Eu estava usando uma DataTable como a fonte de dados. O método EndCurrentEdit estava sendo acionado pelo procedimento EndEditMode, com a seguinte linha de código:

 

Me.BindingContext(CustomerInfo1, _

"Customers").EndCurrentEdit()

 

Isso não funcionava; o DataSet não mostrava nenhuma alteração embora os dados tivessem sido efetivamente alterados. Por fim, percebi que estava usando o contexto errado. O contexto a seguir funcionou sem problemas:

 

Me.BindingContext(CustomerInfo1.Customers).EndCurrentEdit()

 

Nesse caso, estou passando apenas o DataTable, que é de fato a fonte de dados dos controles. O mais interessante é que o código original não gerava nenhum erro quando executado; ele apenas não funcionava. Estou certo de que a linha foi realmente executada porque, quando alterei o nome da tabela (Customers) para CustomersX, a linha gerou um erro de runtime. Como já mencionei, o código estava sendo executado, mas apontava para a fonte de dados errada.

O outro código neste formulário é bastante objetivo, já que ele implementa chamadas para os métodos de navegação e, além disso, trata interações com o CurrencyManager.

O evento cmdAddNew_Click é digno de nota porque faz com que uma nova linha seja adicionada aos dados. Isso limpa o formulário e permite que você inclua dados na nova linha. Você pode ver se as alterações foram automaticamente salvas movendo-se para outra linha e retornando a mesma.

O evento thisCurrencyManager_PositionChanged é acionado quando a posição é alterada. Esse evento é usado para redefinir (reset) o estado do botão SaveChanges.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados