VB.NET - Sincronização automática com Mestre-Detalhe


Este artigo aplica os conceitos sobre o databinding abordados nos artigos :

O gerenciador de banco de dados usado neste artigo é o MSDE onde usamos as tabelas Customers, Orders e Order Details do banco de dados é o Northwind que foi importado do MS Access conforme abordado nos artigos:

Nota: o MSDE foi substituído pelo SQL Server Express que pode ser baixado em : SQL Server Express Edition download

O objetivo deste artigo é mostrar como você pode trabalhar com mais de uma tabela exibindo registros relacionados em uma estrutura Mestre-Detalhe usando o databinding. Vamos exibir os dados tabela Customers com seus dados relacionados na tabela Orders que por sua vez irá exibir os dados relacionados na tabela Orders Details.

Inicie um novo projeto no VS.NET e no formulário padrão insira os seguintes controles (conforme figura abaixo):

Ao executar o projeto o resultado será o exibido no formulário da figura abaixo:

Mas qual é mágica que estar por trás disto ? Como os registros são exibidos sincronizados usando uma estrutura do tipo Mestre-Detalhe ? Como conseguimos exibir os dados relacionados de 3 tabelas diferentes ?

Tratando os relacionamentos entre as tabelas com o DataRelation

Uma das funções primárias do objeto DataRelation é permitir a navegação de um DataTable para outro em um DataSet. Com isto estamos aptos a retornar todos os objetos DataRow relacionados em um DataTable usando um único DataRow de uma tabela relacionada. Assim depois de estabelecer um relacionamento entre a tabela Customers e a tabela Orders podemos retornar todos os registros de pedidos(orders) para um cliente(customer) particular usando o método DataRows.GetChildRows.

Se tivermos dois controles vinculados ao mesmo datasource, e não quisermos compartilhar a mesma posição , devemos estar certos de que o membro BindingContext de um controle difere do outro membro BindingContext do outro controle. Se ambos tiverem o mesmo BindingContext eles irão compartilhar a mesma posição no datasource.

Se incluímos uma Combobox e um DataGrid , como é o caso do nosso exemplo, em um formulário, o comportamento padrão é para que o membro BindingContext de cada um dos dois controles seja definido para o BindingContext do formulário. Assim , o comportamento padrão é para que o DataGrid e o Combobox compartilhem o mesmo BindingContext e dai a seleção na combobox estará sincronizada com a linha atual do DataGrid. Para não termos este comportamento devemos criar um novo membro BindingContext para um dos controles.

O código do projeto que cria os relacionamentos no DataSet entre as tabelas Customers, Orders e Orders Details é o seguinte :

        'define o relacionamento entre as tabelas Customers e Orders
        Dim relCustOrd As System.Data.DataRelation
        Dim colMestre As System.Data.DataColumn
        Dim colDetalhe As System.Data.DataColumn

        colMestre = ds.Tables("Customers").Columns("CustomerID")
        colDetalhe = ds.Tables("Orders").Columns("CustomerID")
        relCustOrd = New System.Data.DataRelation("RelCustOrd", colMestre, colDetalhe)

        'inclui o relacionamento entre a tabela Customers e Orders no dataset
        ds.Relations.Add(relCustOrd)

        'define o relacionamento entre as tabelas Orders e Orders Details
        Dim relOrdDet As System.Data.DataRelation
        Dim colMestre2 As System.Data.DataColumn
        Dim colDetalhe2 As System.Data.DataColumn

        colMestre2 = ds.Tables("Orders").Columns("OrderID")
        colDetalhe2 = ds.Tables("OrderDetails").Columns("OrderID")
        relOrdDet = New System.Data.DataRelation("RelOrdDet", colMestre, colDetalhe2)

        'inclui o relacionamento entre a tabela Orders e Orders Details no dataset
        ds.Relations.Add(relOrdDet)

         .......

Ele procurar reproduzir o relacionamento entre as tabelas:

Naturalmente o código que cria a conexão, o dataset  já esta anteriormente definido conforme abaixo:

       ConnectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Northwind; _
                                  User ID=sa;password=;Data Source=MACORATI\VSDOTNET"

        Dim cn As SqlConnection = New SqlConnection(ConnectionString)
        ds = New DataSet("CustOrders")
        Dim da1 As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", cn)
        da1.TableMappings.Add("Table", "Customers")
        da1.Fill(ds)

        Dim da2 As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Orders", cn)
        da2.TableMappings.Add("Table", "Orders")
        da2.Fill(ds)
        Dim da3 As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM [Order Details]", cn)
        da3.TableMappings.Add("Table", "OrderDetails")
        da3.Fill(ds)

        .......

A seguir basta efetuar a vinculação dos dados com o DataGrid e a Combobox.

O exemplo exibe um DataGrid para os Pedidos (Orders) e outro para os detalhes(Orders Details). Os dados do cliente são exibidos em controles TextBox e Combobox.

Ao alterar o cliente selecionado os dados de cada DataGrid são atualizados e exibem os pedidos para o cliente atual. Afim de vincular os dois objetos DataGrids devemos definir o DataSource de cada DataGrid para o mesmo DataSet. Também devemos definir as propriedades do DataMembr para indicar o BindingContext com o qual eles estão relacionados. Fazemos isto definindo o DataMember para os DataGrids para o nome do relacionamento entre as tabelas Customers e Orders; o mesmo é feito para a tabela Orders e Orders Details.

        dsView = ds.DefaultViewManager
        'vinculando as tabelas e seus relacionamentos
        dgPedidos.DataSource = dsView
        dgPedidos.DataMember = "Customers.RelCustOrd"
        dgDetalhesPedidos.DataSource = dsView
        dgDetalhesPedidos.DataMember = "Customers.RelCustOrd.RelOrdDet"
        'vinculação da ComboBox
        cbNome.DataSource = dsView
        cbNome.DisplayMember = "Customers.CompanyName"
        cbNome.ValueMember = "Customers.CustomerID"

         'vinculação dos TextBox
        txtContato.DataBindings.Add("Text", dsView, "Customers.ContactName")
        txtFone.DataBindings.Add("Text", dsView, "Customers.Phone")
        txtFax.DataBindings.Add("Text", dsView, "Customers.Fax")

 


Para controlar a navegação definimos os códigos presentes no evento Click de cada um dos botões de comando:
 

Private Sub btnAnterior_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAnterior.Click
        If Me.BindingContext(dsView, "Customers").Position > 0 Then
            System.Math.Max(System.Threading.Interlocked.Decrement(Me.BindingContext(dsView, "Customers").Position), _
                 Me.BindingContext(dsView, "Customers").Position + 1)
        End If
    End Sub

Botão Anterior

   

Private Sub btnProximo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProximo.Click
        Dim cm As CurrencyManager = CType(Me.BindingContext(dsView, "Customers"), CurrencyManager)
        If cm.Position < cm.Count - 1 Then
            System.Math.Min(System.Threading.Interlocked.Increment(cm.Position), cm.Position - 1)
        End If
    End Sub

 

Botão Próximo

Com isto acabamos de mostrar como você conseguiu , sem muito esforço, tratar os dados de 3 tabelas relacionadas exibindo-os em uma estrutura Mestre-Detalhe sincronizada.

Use o seu talento e incremente a aplicação incluindo o tratamento de erros e outros detalhes...

Pegue o código completo do projeto aqui : dbMestreDetalhe.zip

Eu sei é apenas VB.NET mas eu gosto ...

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 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?


  Gostou
?   Compartilhe no Facebook   Compartilhe no Twitter

 

Referências:


José Carlos Macoratti