Expandir
Novos recursos de DataSet no Visual Studio 2005
Visual Studio 2005

Jackie Goldstein - Renaissance Computer Systems

Fevereiro de 2005

Aplica-se a:

  • Microsoft ADO.NET 2.0

  • Microsoft Visual Studio 2005 (Beta)

Resumo: aprenda sobre os novos recursos da classe DataSet de tipos e da nova classe TableAdapter que são geradas pelo Microsoft Visual Studio 2005, bem como as ferramentas para a criação dessas classes. Aprenda também sobre os novos componentes BindingSource e BindingNavigator e veja como usá-los para criar rapidamente aplicativos WinForms flexíveis e com limite de dados. Este artigo também contém links para páginas em inglês (20 páginas impressas).

Salvo indicação em contrário, o conteúdo deste artigo tem como base a versão CTP (Community Technology Preview) de dezembro de 2004 do Visual Studio 2005.

O código de exemplo abordado neste artigo está disponível para download aqui.

Nesta página

IntroduçãoIntrodução
Fontes de dadosFontes de dados
Criando formulários centrados em dadosCriando formulários centrados em dados
Personalizando o código geradoPersonalizando o código gerado
ConclusãoConclusão

Introdução

Em um artigo anterior, Novos recursos de DataSet no ADO.NET 2.0, abordei algumas das alterações e dos aperfeiçoamentos futuros na classe DataSet do ADO.NET e nas classes associadas, como DataSet, DataTable e DataView. Todas essas classes fazem parte da Biblioteca de Classes Base do Microsoft .NET Framework.

Neste artigo, nos concentraremos no desenvolvimento com essas classes e com as classes derivadas no ambiente de desenvolvimento do Microsoft Visual Studio 2005. Especificamente, neste artigo abordaremos as alterações no DataSet de tipos e o novo TableAdapter de tipos que são gerados pelo Visual Studio 2005. Também examinaremos os designers e as ferramentas que oferecem flexibilidade e produtividade incríveis para o desenvolvimento dos aspectos centrados em dados de seu aplicativo. Para explicar os diferentes conceitos e recursos, passarei pelo processo pelo qual o desenvolvedor geralmente passa ao implementar a parte de dados de um aplicativo. Os exemplos de código usam o banco de dados Northwind fornecido como um banco de dados de exemplo no Microsoft SQL Server (e MSDE) 7.0 e 2000.

Fontes de dados

O Visual Studio 2005 apresenta o conceito de fontes de dados em um projeto. Uma fonte de dados representa os dados disponíveis para um aplicativo. Esses dados não estão necessariamente em um banco de dados, o Data Source Configuration Wizard (Assistente para Configuração de Fonte de Dados) que você usa para definir a fonte de dados permite obter os dados de três fontes diferentes:

  1. Banco de dados — pode ser um banco de dados baseado no servidor, como o SQL Server ou o Oracle, ou um banco de dados baseado em arquivo, como o Access ou o SQL Server Express. O Visual Studio gera automaticamente os DataSets de tipos, bem como outras classes, e os adiciona ao projeto.

  2. Objeto — qualquer objeto com propriedades públicas pode ser a fonte de dados. Não é necessário implementar nenhuma interface especial.

  3. Serviço da Web — criar uma fonte de dados em um serviço da Web cria objetos correspondentes ao tipo de dados retornado por esse serviço.

A finalidade da fonte de dados é dupla. Em primeiro lugar, ela torna mais fácil especificar, criar e gerar classes com rigidez de tipos que representam os dados do aplicativo. Em segundo lugar, fornece um mecanismo flexível, porém uniforme, de criação rápida de interfaces de usuário WinForm e WebForms avançadas e altamente funcionais. Neste artigo, veremos o quanto isso é rápido, fácil e flexível.

Também nos concentraremos na criação de fontes de dados de banco de dados (DataSet) e no uso dessas fontes em aplicativos WinForms. No entanto, é importante salientar estes dois pontos:

  • Após a criação de uma fonte de dados, você a utiliza da mesma maneira, independentemente da origem dos dados. Ou seja, assim como você pode facilmente (e graficamente) ligar uma fonte de dados baseada em um Database a uma grade ou a um conjunto de controles, também pode fazer isso com os dados cuja origem é um serviço da Web ou seus objetos comerciais personalizados.

  • As fontes de dados são definidas da mesma maneira, independentemente de serem usadas em um aplicativo WinForms ou WebForms. Os diferentes provedores de dados também são abstratos; assim, se o seu acesso aos dados estiver exposto somente pelo uso de DataSets e TableAdapters, para alterar o banco de dados real, bastará alterar a seqüência de caracteres de conexão e gerar as classes novamente.

DataSet e TableAdapter de tipos

Uma fonte de dados de banco de dados é a combinação de um DataSet com rigidez de tipos e um ou mais pares de DataTables e TableAdapters com rigidez de tipos. A idéia de um DataSet de tipos não é nova, ou seja, já estava presente no Visual Studio 2002/2003. Um DataSet de tipos é uma classe gerada a partir de uma classe DataSet genérica do .NET Framework, mas que tem um esquema definido, bem como propriedades e métodos específicos desse esquema. Ao mesmo tempo, para cada tabela no DataSet, três classes derivadas adicionais são geradas: DataTable, DataRow e DataRowChangeEvent, que são específicas desse DataSet. Cada uma dessas classes tem esquema, propriedades e métodos específicos da tabela associada. Por exemplo, se eu definir uma fonte de dados com base na tabela de funcionários do Northwind, serão geradas as seguintes classes:

  • NorthwindDataSet

  • EmployeesDataTable

  • EmployeesDataRow

  • EmployeesRowChangeEvent

Essas quatro classes formam o DataSet de tipos. No Visual Studio 2005, há uma quinta classe que também é gerada, o TableAdapter de tipos chamado EmployeesTableAdapter, o qual abordaremos brevemente. Obviamente, se você estiver definindo consultas de forma dinâmica, não poderá gerar os DataSets de tipos e terá de usar o DataSet padrão.

Por que se preocupar com um DataSetde tipos? Além de forçá-lo a pensar no esquema dos dados previamente, em vez de apenas improvisar, um DataSet de tipos oferece várias vantagens concretas:

  1. DataSets, DataTables, DataRows e RowChangeEvent são específicos do esquema que está sendo processado.

  2. Tabelas, colunas e relações são expostas como propriedades nomeadas, em vez de elementos genéricos da coleção.

  3. Como resultado de (2), você tem suporte total a IntelliSense e à conclusão da instrução no editor de códigos do Visual Studio.

  4. Também como um resultado de (2), a verificação de tipos no tempo de compilação é possível (por exemplo, o erro ortográfico em um nome de campo é detectado no tempo de compilação, em vez de no tempo de execução).

  5. O código é mais conciso e legível; em vez de:

    country = dsNorthwind.Tables ("Employees").Rows (row) ("Country")

    você tem:

    country = dsNorthwind.Employees (row).Country

O resultado é que a assistência em tempo de design e em tempo de compilação oferecida pelos DataSets reduzirá significativamente não apenas o tempo inicial de desenvolvimento, mas também o tempo necessário para depurar e estabilizar o aplicativo.

O conceito de um TableAdapter, por outro lado, é novo no Visual Studio 2005. A idéia subjacente é que um TableAdapter com rigidez de tipos é o equivalente com rigidez de tipos do DataAdapter padrão. Você usa o TableAdapter para se conectar a um banco de dados e executar consultas (ou procedimentos armazenados) nesse banco de dados, bem como para preencher com dados um DataTable associado. Cada par DataTable-TableAdapter é indicado simplesmente como um TableAdapter.

Observação: no CTP de dezembro de 2004, o par DataTable-TableAdapter é indicado como um Data Component (Componente de Dados). No entanto, o uso desse termo foi eliminado do Visual Studio e as compilações futuras irão se referir ao par apenas como um TableAdapter. Neste artigo, somente o termo TableAdapter será usado.

O TableAdapter é essencialmente um invólucro ao redor de um DataAdapter padrão, que oferece vários benefícios:

  • A mesma classe TableAdapter pode ser usada em mais de um formulário ou componente para que qualquer alteração em consultas/comandos seja automaticamente refletida em todas as instâncias. Essa situação é diferente da existente, na qual cada componente que acessa o banco de dados deve ter seu próprio DataAdapter configurado individualmente. Dessa forma, fica muito mais fácil garantir a sincronização de DataTables e DataAdapters.

  • Em vez de usar vários DataAdapters (ou código de comutação artesanal) para ter várias consultas/comandos para um único DataTable, um TableAdapter permite definir facilmente vários comandos para um DataTable específico.

  • Os comandos de preenchimento têm nomes legíveis ("amigáveis") e o TableAdapter inclui um código para preencher automaticamente as informações de tipo e valor de todos os parâmetros desses métodos de comando. Você não precisa mais se preocupar em passar tipos de dados específicos do provedor, como SqlInt.

Um simples trecho de código ajudará a ilustrar esses recursos. No Visual Studio 2002/2003, mesmo usando um DataSet de tipos, o código para executar uma simples consulta com dois parâmetros não seria simples. Para a consulta:

SELECT FirstName, LastName from Employees WHERE Country = @country AND City = @city

precisaríamos escrever algo como:

Me.SqlAdapter1.SelectCommand.Parameters ("@country").value =     
Me.CountryListbox.SelectedValue.Trim()   
Me.SqlAdapter1.SelectCommand.Parameters ("@city").value =     
Me.CityTextbox.Text.Trim()   
Me.SqlAdapter1.Fill (Me.NorthwindDataSet.Employees)

Obviamente, à medida que o número de parâmetros aumenta, também aumenta o número de linhas do código. Mas o mais importante é que o trabalho de lembrar e digitar corretamente cada nome de parâmetro fica bastante reduzido. Mesmo que eu tenha o nome certo do parâmetro, ainda tenho de lembrar o respectivo tipo de dados. O pior de tudo é que se eu inserir incorretamente o nome do campo ou tentar atribuir um valor de tipo errado, só descobrirei isso no tempo de execução.

Com o TableAdapter no Visual Studio 2005, depois de definir o comando FillByCountryAndCity, para usá-lo em qualquer lugar, basta escrever uma linha de código passando os valores de parâmetro:

Me.EmployeesTableAdapter.FillByCountryAndCity ( _
   Me.NorthwindDataSet.Employees, Me.CountryListbox.SelectedValue.Trim(), _    Me.CityTextbox.Text.Trim() )

É importante observar que não somente obtemos vários comandos nomeados de um único TableAdapter, mas que esses comandos têm rigidez de tipos. Isso significa que, ao escrevermos um código no Visual Studio, temos todo o IntelliSense para ver esses comandos como métodos do TableAdapter. Também obtemos a verificação de tipos dos parâmetros para esses comandos no tempo de compilação, bem como a dica de ferramenta com as definições de método e tipo de parâmetro para nos ajudar. O TableAdapter pode ter vários métodos que executam comandos diferentes e aceitam parâmetros diferentes. Examinaremos melhor o TableAdapter um pouco mais adiante, ao criarmos nosso formulário de exemplo.

Guia de Introdução — Criando uma fonte de dados

Neste artigo, vamos criar um formulário para exibir informações sobre cada um dos pedidos do banco de dados Northwind. Nossa primeira ação, depois de abrir um novo projeto WinForms do Visual Basic, será adicionar uma nova fonte de dados ao nosso projeto (usaremos aqui o Visual Basic, mas também tudo que se aplica ao C#).

Para adicionar uma fonte de dados:

  1. Exiba a janela Data Sources (Fontes de Dados) (caso ainda não esteja aberta) selecionando Show Data Sources (Mostrar Fontes de Dados) no item de menu Data (Dados) do menu principal do Visual Studio.

  2. Na janela Data Sources, clique no botão Add New Data Source (Adicionar Nova Fonte de Dados) da barra de ferramentas. Isso iniciará o Data Source Configuration Wizard, o qual combina grande parte da funcionalidade do DataAdapter Configuration Wizard (Assistente para Configuração do DataAdapter) e das ferramentas de geração de DataSet do Visual Studio 2002/2003.

  3. Se a sua compilação do Visual Studio incluir também uma página de boas-vindas nos assistentes, selecione Next (Avançar). Isso exibirá a página Choose a Data Source Type (Escolher um Tipo de Fonte de Dados).

  4. Selecione Database (Banco de Dados).

  5. Selecione Next. Isso exibirá a página Choose Your Data Connection (Escolher a Conexão de Dados).

  6. Selecione New Connection (Nova Conexão). Isso exibirá a caixa de diálogo Add Connection (Adicionar Conexão).

  7. Insira as informações para se conectar à sua instância do SQL Server ou MSDE e ao banco de dados Northwind.

  8. Selecione OK para fechar a caixa de diálogo.

  9. Lembre-se de que a seqüência de caracteres de conexão agora está salva como uma propriedade de configurações e pode ser acessada por:

    My.Settings.NorthwindConnectionString

    No C#, seria:

    VSDataSets.Properties.Settings.Default.NorthwindConnectionString;

  10. Selecione Next. Isso exibirá a página Choose Your Database Objects (Escolher os Objetos de Banco de Dados).

  11. Observe que você pode escolher entre Tables, Views, Stored Procedures (Tabelas, Modos de Exibição, Procedimentos Armazenados) ou Functions (Funções).

    Expanda o nó Tables (Tabelas) e selecione a tabela de pedidos e a de detalhes do pedido. Usaremos todas as colunas das tabelas, mas você pode selecionar apenas as colunas necessárias para seu aplicativo.

  12. Selecione Finish (Concluir) para sair do assistente.

A Figura 1 mostra a janela Data Sources resultante com a tabela de detalhes do pedido expandida para exibir todas as suas colunas.


Figura 1. Tabela de detalhes do pedido na janela Data Sources

Para acessar novamente o Data Source Configuration Wizard a fim de fazer algumas alterações, basta selecionar Configure DataSet with Wizard (Configurar DataSet com Assistente) na barra de ferramentas da janela Data Sources ou o menu de contexto exibido quando você clica com o botão direito do mouse em qualquer elemento. No entanto, uma ferramenta mais avançada para edição de DataSet e TableAdapter(s) gerados é o DataSet Designer (Designer de DataSet).

O DataSet Designer

O Visual Studio 2005 inclui o DataSet Designer, uma ferramenta realmente criada para especificar e editar DataSets e seus TableAdapters associados. Essa é uma melhoria significativa em relação ao Visual Studio 2002/2003, no qual éramos forçados a usar um editor de esquemas XML para definir DataSets com rigidez de tipos. As definições de DataSet e TableAdapter ainda são salvas em um arquivo .XSD e ainda há um editor XML para quando você quiser realmente editar esquemas XML. Mas as semelhanças param por aqui. A intenção não é o DataSet Designer oferecer suporte a arquivos XSD arbitrários, mas que este seja, preferivelmente, um formato de arquivo conveniente.

Para abrir o designer de DataSet, basta selecionar Edit DataSet with Designer (Editar DataSet com Designer) na barra de ferramentas da janela Data Sources ou no menu de contexto que você acessa ao clicar com o botão direito do mouse em qualquer elemento dessa janela. Ao criar DataSets e seus DataTables, o designer deve agir praticamente da mesma forma que as outras ferramentas que você já usou para criar bancos de dados. Você pode adicionar uma tabela do banco de dados ao qual está conectado arrastando um objeto do banco de dados (como tabela, modo de exibição ou procedimento armazenado) do Server Explorer (Explorador do Servidor) para a superfície do designer ou iniciando o TableAdapter Configuration Wizard (Assistente para Configuração de TableAdapter) selecionando Add TableAdapter (Adicionar TableAdapter) no item de menu Data do menu de contexto do designer. Como um DataSet também pode ter tabelas carregadas diretamente e não conectadas a um banco de dados, você também pode adicionar uma tabela independente selecionando Add DataTable (Adicionar DataTable) nos menus. Obviamente, você também pode adicionar e/ou renomear colunas no editor. Outro bom recurso é que o editor reconhece automaticamente as relações entre as tabelas do banco de dados e define para você as Relações correspondentes entre as tabelas do DataSet.

A Figura 2 mostra nossa fonte de dados, que consiste nas tabelas de pedidos e de detalhes do pedido, no designer de DataSet. Observe que, intimamente associado a cada DataTable, há o TableAdapter correspondente usado para preencher a tabela com dados e, opcionalmente, atualizar o banco de dados com as alterações feitas nesses dados.


Figura 2. Tabelas de pedidos e de detalhes do pedido na fonte de dados

O TableAdapter Configuration Wizard

Você inicia o TableAdapter Configuration Wizard (ainda chamado de Data Component Configuration Wizard (Assistente para Configuração de Componentes de Dados) na versão CTP de dezembro de 2004 do Visual Studio 2005) no designer de DataSet selecionando Add Query (Adicionar Consulta) ou Configure (Configurar) (uma consulta existente) no menu principal Data ou no menu de contexto que você acessa clicando com o botão direito do mouse em um TableAdapter no designer. Esse assistente é idêntico ao DataAdapter Configuration Wizard do Visual Studio 2002/2003, com exceção das duas páginas adicionais que oferece. A primeira página é Choose a Query Type (Escolher um Tipo de Consulta), mostrada na Figura 3. Como o TableAdapter é o ponto central de todos os comandos relacionados a uma determinada tabela, ele permite não somente definir vários comandos Select/Fill (Selecionar/Preencher), mas também várias consultas de qualquer tipo, como Update, Insert, Delete (Atualizar, Inserir, Excluir) ou qualquer uma que retorne um único valor. Lembre-se de que essas consultas de atualização são métodos nomeados do TableAdapter e devem ser chamadas diretamente. Elas são um complemento das consultas de atualização chamadas automaticamente quando você executa o método Update do TableAdapter, como no método DataAdapter.Update.

A segunda página adicional do assistente é a página Choose Methods to Generate (Escolher Métodos a Serem Gerados). É nela que você escolhe o(s) nome(s) de cada método de consulta/comando que define. O assistente oferece os métodos Fill e Get para cada comando, como mostra a Figura 3. O método Fill exige o preenchimento do DataTable, enquanto a versão Get retornará um DataTable recém-criado e preenchido.


Figura 3. Métodos Fill e Get na página Choose Methods to Generate

No uso comum, você definirá vários comandos Fill para um TableAdapter, o qual retorna o mesmo esquema (colunas), mas tem cláusulas WHERE diferentes. É por isso que, por padrão, o assistente oferece um prefixo de FillBy e GetDataBy para os nomes de método. Obviamente, você tem a liberdade de atribuir aos métodos os nomes de sua preferência.

Embora um TableAdapter possa ter vários comandos Fill, há apenas um conjunto de comandos de atualização executados quando o método Update do TableAdapter é chamado. Eles são gerados automaticamente com base na consulta principal do TableAdapter. A consulta definida quando o TableAdapter é criado pela primeira vez é considerada sua consulta principal. Se qualquer consulta definida subseqüentemente retornar um esquema diferente do esquema da consulta principal, o designer exibirá uma caixa de mensagem de aviso. Como alternativa, se você modificar o esquema da consulta principal, o Visual Studio modificará suas outras consultas para que correspondam ao esquema.

Adicionando um novo comando usando o TableAdapter Configuration Wizard

Agora vamos adicionar outro comando ao TableAdapter da tabela de pedidos.

  1. Abra o designer do DataSet selecionando Edit DataSet with Designer na barra de ferramentas da janela Data Sources.

  2. Selecione o TableAdapter da tabela de pedidos e escolha Add Query em seu menu de contexto.

  3. Selecione Next na página Welcome (de boas-vindas) caso essa página ainda exista em sua compilação do Visual Studio. Isso exibirá a página Choose a Command Type (Escolher um Tipo de Comando).

  4. Selecione Next para aceitar o padrão das instruções SQL. Isso exibirá a página Choose a Query Type.

  5. Selecione Next para aceitar a instrução SELECT padrão. Isso exibirá a página Specify a SQL SELECT Statement (Especificar uma instrução SQL SELECT).

  6. Insira a seguinte instrução SQL:

    SELECT OrderID, CustomerID, EmployeeID, OrderDate, RequiredDate, ShippedDate, ShipVia, Freight, ShipName, 
    ShipAddress, ShipCity, ShipRegion, ShipPostalCode, ShipCountry 
    FROM Orders
    WHERE CustomerID = @CustID
    

    Isso retornará para a consulta todos os pedidos do cliente especificado pelo parâmetro @CustID.

  7. Selecione Next. Isso exibirá a página Choose Methods to Generate.

  8. Deixe ambas as caixas de seleção marcadas. Modifique os nomes dos métodos para FillByCustomer e GetDataByCustomer, respectivamente.

  9. Selecione Next e Finish para concluir o processo.

Se você observar OrdersTableAdapter no designer de DataSet, verá que agora ele mostra um segundo par de comandos, FillByCustomer e GetDataByCustomer, que utiliza um valor CustomerID como parâmetro. O que é o tipo (.NET) desse parâmetro do método? Vamos dar uma olhada.

O que está nos bastidores

Quando você faz alterações no designer de DataSet ou em qualquer um dos assistentes associados, o Visual Studio gera um código para um conjunto de classes de tipos. Especificamente, ele gera as seguintes classes:

  1. Uma classe DataSet

  2. Uma classe DataTable

  3. Uma classe TableAdapter

  4. Uma classe DataRow

  5. Uma classe DataRowChangeEvent

Com exceção da classe DataSet, que é apenas uma por fonte de dados, cada uma das outras quatro classes se repete para cada tabela definida no DataSet. Você pode ver o código dessas classes fazendo o seguinte:

  • Na janela Solution Explorer (Explorador da Solução), clique no botão Show All Files (Mostrar Todos os Arquivos) para exibir todos os arquivos associados ao projeto.

  • Expanda o nó do arquivo NorthwindDataSet.xsd.

  • Clique duas vezes no nó do arquivo de NorthwindDataSet.Designer.vb. Esse é o código gerado para implementar as classes que formam nosso DataSet.

  • Abra a caixa de listagem no lado superior esquerdo da janela de código. Nela, você verá a lista de classes desse arquivo:

    • NorthwindDataSet

    • Order_DetailsDataTable

    • Order_DetailsRow

    • Order_DetailsRowChangeEvent

    • Order_DetailsTableAdapter



    • OrdersDataTable

    • OrdersRow

    • OrdersRowChangeEvent

    • OrdersTableAdapter

  • Selecione a classe OrdersTableAdapter na caixa de listagem esquerda.

  • Selecione o método FillByCustomer na caixa de listagem direita. Isso exibirá o código desse método, como mostrado a seguir:

    Public Overloads Overridable Function FillByCustomer(ByVal dataTable 
             As NorthwindDataSet.OrdersDataTable, ByVal CustID As String) As Integer
                Me.Adapter.SelectCommand = Me.CommandCollection(1)
                If (CustID Is Nothing) Then
                    Throw New System.ArgumentNullException("CustID")
                Else
                    Me.Adapter.SelectCommand.Parameters(0).Value = CType(CustID,String)
                End If
                If (Me.m_clearBeforeFill = true) Then
                    dataTable.Clear
                End If
                Dim returnValue As Integer = Me.Adapter.Fill(dataTable)
                Return returnValue
            End Function
    

Podemos aprender algumas coisas com esse trecho de código.

  • Os métodos FillByCustomer, na verdade, utilizam dois parâmetros — um OrdersDataTable a ser preenchido e um parâmetro CustID, que é uma seqüência de caracteres.

  • Sem precisar analisar todo o código do arquivo, constatamos que a classe TableAdapter mantém um conjunto de comandos, a partir do qual ela atribui automaticamente o comando correto ao DataAdapter do .NET que está usando para comunicar-se realmente com o banco de dados.

  • O método verifica se uma instância de um parâmetro de estado foi passada para o método (se a propriedade AllowDBNull do parâmetro está definida como False).

  • O valor do parâmetro CustID é atribuído ao parâmetro (já configurado) da propriedade SelectCommand do DataAdapter.

  • Quando a configuração é concluída, o método DataAdapter.Fill () padrão é chamado para preencher OrdersDataTable.

Lembre-se de que esse código não precisa ser escrito por você. Tudo já foi gerado e configurado. Aproveite para dar uma olhada no código das outras classes que foram geradas para você. Você compreenderá melhor como essas classes são implementadas e, provavelmente, também encontrará uma ou duas boas idéias de codificação.

Observação: embora o DataSet de tipos e suas classes associadas, incluindo TableAdapters, sejam todos gerados em um único arquivo de origem, os TableAdapters são gerados em um espaço para nome separado. Isso reflete a necessidade de uma separação entre os objetos de entidade (DataSets) e os objetos reais de acesso a dados (TableAdapters).

Criando formulários centrados em dados

Agora que já geramos um DataSet de tipos, chegou a hora de criar um formulário para exibir esses dados. Embora eu não vá me aprofundar em todos os detalhes e novos recursos do WinForms e na ligação de dados do .NET Framework 2.0 e do Visual Studio 2005 (e há muito para se aprofundar), esta é uma boa oportunidade para ver um pouco do que tem sido feito para tornar a criação de formulários funcionais centrados em dados muito mais fácil e flexível.

Componentes de dados na caixa de ferramentas

Se você está acostumado a criar seu código centrado em dados começando pelos componentes de dados padrão da guia Data (Dados) da caixa de ferramentas, talvez fique um pouco aflito e preocupado ao abrir o Visual Studio 2005 e não encontrá-los lá. Obviamente, isso faz parte do design. A Microsoft quer nos incentivar a aproveitar os novos DataSets e TableAdapters de tipos. Se você realmente quiser usar componentes antigos sem tipos, poderá adicioná-los manualmente usando a caixa de ferramentas. Eu sugiro que você não faça isso... Como aproveitam os novos TableAdapters juntamente com tecnologia de classe parcial, os novos DataSets de tipos e TableAdapters são muito mais fáceis de usar e ampliar.

Você encontrará mais informações sobre essas e outras decisões de design, bem como as idéias que levaram a elas, no blog da equipe (em inglês) do Visual Basic.

Especificamente, leia as duas publicações de Steve Lasker: Why are the Data Components no longer on the Toolbox? (em inglês) e Why can't I drag from Server Explorer to my form? (em inglês).

Quando você abre um formulário no designer do Visual Studio, a caixa de ferramentas do programa exibe uma guia cujo rótulo é o nome do seu projeto. Depois de adicionar uma fonte de dados ao projeto e compilá-lo pelo menos uma vez, essa guia incluirá os DataSets e TableAdapters de tipos criados. Embora você possa arrastar esses componentes para o designer de formulário, talvez isso não seja o que fará normalmente (se, no entanto, estiver implementando um componente de acesso a dados usando o designer, será uma boa opção). Normalmente, você usará uma das três diferentes abordagens para criar um formulário centrado em dados. Vamos dar uma olhada no primeiro método, que é o mais fácil e talvez o mais comum. É o que chamamos de ligação de dados "Drag Once" (arrastar uma vez).

  1. Clique duas vezes em Form1.vb no Solution Explorer para abrir Form1 no Form Designer (Designer de Formulários) do Visual Studio.

  2. Na janela Data Sources, expanda o nó da tabela de pedidos.

    Observe que há um ícone associado a cada tabela e cada coluna do DataSet. Esses ícones representam o tipo de controle do WinForm (ou "tipo de liberação") que será usado para ligar aos dados se a tabela ou a coluna for arrastada e soltada em um formulário. Você pode alterar o tipo de controle selecionando um item e escolhendo um controle na lista suspensa associada. Observe que a lista inclui opções para None (nada exibido) bem como para Customize (você especifica o controle desejado).

    Esses ícones e a lista de tipos de liberação estarão visíveis somente se a janela ativa atual for um designer de formulários (ou componente). Caso contrário, você não poderá arrastar e soltar controles da janela Data Sources e os ícones mudarão para indicar isso.

  3. Altere o tipo de controle da tabela de pedidos de DataGridView para Details. Isso significa que, quando você arrastar toda a tabela de pedidos para um formulário, em vez de criar um formulário que mostre todos os dados em uma grade (todas as linhas de uma vez), criará um formulário que exibe os detalhes das linhas, uma por vez. Em um modo de exibição Details, um rótulo e um controle serão adicionados para cada coluna e o tipo de controle será o especificado na janela Data Sources.

  4. Arraste a tabela de pedidos da janela Data Sources para o Form1 no designer.

  5. Selecione os últimos sete campos (juntamente com seus rótulos) e arraste-os ao longo dos primeiros sete campos para que o formulário fique com a aparência mostrada na Figura 4.


    Figura 4. Form1 no designer

  6. Inicie o aplicativo e verifique se ele funciona percorrendo os registros com os botões de navegação da barra de ferramentas exibida na parte superior do formulário.

    Vamos ver o que o Visual Studio fez por nós quando arrastamos uma tabela da fonte de dados para um formulário. Ao observamos a bandeja de componentes abaixo do formulário, vemos que ele adicionou quatro componentes ao formulário. Nós já conhecemos (e amamos?) dois desses componentes: NorthwindDataSet e OrdersTableAdapter. O OrdersTableAdapter é usado para preencher a tabela OrdersDataTable do NorthwindDataSet com dados do banco de dados. Até mesmo a única linha de código para executar esse Fill (preenchimento) já foi escrita e automaticamente adicionada ao manipulador de eventos Load de Form1.

    Me.OrdersTableAdapter.Fill(Me.NorthwindDataSet.Orders)
    

A classe circular para a ligação de dados é a BindingSource, a qual, em nosso caso, recebe o nome de OrdersBindingSource. O BindingSource (conhecido como DataConnector na versão beta 1) fornece os serviços necessários para ligar controles em um formulário. Ele fornece uma camada de ação indireta entre uma fonte de dados e os controles ligados a ela. Você anexa o BindingSource a uma fonte de dados definindo as propriedades DataSource e DataMember do BindingSource e, em seguida, liga os controles ao BindingSource adicionando a coleção DataBindings dos controles. Toda interação com os dados, como navegação, classificação, filtragem e edição de registros, é feita através de BindingSource. O BindingSource também permite acessar dados subjacentes através das propriedades List, Item e Current.

O outro componente adicionado é o OrdersBindingNavigator. Uma classe BindingNavigator é uma barra de ferramentas que fornece uma interface de usuário padrão para navegar e manipular dados em um formulário. O BindingNavigator (conhecido como DataNavigator na versão beta 1) é um controle ToolStrip com um conjunto de botões pré-configurados. Ele conecta-se a um BindingSource como a fonte de seus dados e fornece botões da barra de ferramentas para controle da navegação pelos dados disponíveis. Se, no entanto, em vez de controlar a navegação, desejar responder a alguns eventos de navegação, você deverá criar ganchos de eventos do objeto BindingSource.

Criando um formulário Master-Details

Agora que temos um formulário que exibe dados de uma única tabela, é fácil (ou difícil) exibir dados de uma segunda tabela relacionada em um formato Master-Details? Basta seguir com estas etapas no Form1:

  1. Usaremos agora a ligação de dados de conectar os pontos para criar o formulário, ou seja, arrastaremos um controle da caixa de ferramentas e o colocaremos no formulário. Em seguida, arrastaremos um elemento da janela Data Source e o soltaremos nesse controle para conectá-los.

  2. Selecione o controle DataGridView na guia All Windows Forms (Todos os Formulários do Windows) da caixa de ferramentas. Arraste-o para Form1 e posicione-o de forma a ocupar a maior parte da metade inferior do formulário.

  3. Volte para a janela Data Source, como mostra a Figura 5. Observe que a tabela Order Details (Detalhes do Pedido), na verdade, é exibida duas vezes nessa janela. Na primeira vez, ela é uma filha direta do NorthwindDataSet e uma irmã da tabela de pedidos. Na segunda vez, ela é exibida como uma filha da tabela de pedidos, indicando ser uma tabela relacionada. Para exibir independentemente a tabela de detalhes do pedido no formulário, poderíamos escolher sua ocorrência logo abaixo de NorthwindDataSet. No entanto, quando, como em nosso caso, queremos exibir a tabela de detalhes do pedido como relacionada à tabela de pedidos (mestre), escolhemos a ocorrência da tabela de detalhes do pedido logo abaixo da tabela de pedidos.


    Figura 5. Tabela de detalhes do pedido na janela Data Source

  4. Selecione a tabela Order Details (Detalhes do Pedido) exibida abaixo da tabela Orders (Pedidos) e arraste-a para DataGridView no Form1.

  5. Observe o Order_DetailsBindingSource e o Order_detailsTableAdapter, que foram adicionados à bandeja de componentes do Form1.

  6. Execute o aplicativo e use o BindingNavigator para se mover pelos registros da tabela Orders, como mostra a Figura 6. Observe como os registros da tabela de detalhes do pedido exibidos no DataViewGrid mudam automaticamente para exibir apenas os relacionados ao registro atual da tabela de pedidos.


    Figura 6. BindingNavigator na tabela de pedidos

Por alguma razão, a ligação de dados de conectar os pontos foi eliminada na versão CTP de dezembro de 2004 do Visual Studio 2005. No entanto, ela funciona corretamente nas versões anteriores e nas versões mais recentes do Visual Studio 2005. Se você estiver usando a versão CTP de dezembro, arraste a tabela de detalhes do pedido (abaixo da tabela de pedidos) e solte-a no Form1.

Personalizando o código gerado

Anteriormente, quando procuramos o código para as classes de nosso DataSet, você deve ter notado que há, na verdade, dois arquivos de código do Visual Basic, NorthwindDataSet.Designer.vb e NorthwindDataSet.vb. Se o arquivo NorthwindDataSet.vb não estiver presente, volte para o DataSet Designer e clique duas vezes no plano de fundo do designer para fazer com que o arquivo seja criado.

Os dois arquivos são usados para implementar as classes que compõem nosso DataSet. A razão para haver dois arquivos é aproveitar um recurso novo, simples, mas bastante eficiente chamado classes parciais. As classes parciais são um recurso compilador que permite definir uma classe (ou estrutura) a ser dividida entre várias declarações. Declarações diferentes podem estar em arquivos de código-fonte diferentes, desde que as declarações estejam todas no mesmo conjunto de módulos (assembly) e no mesmo espaço para nome. O Visual Studio faz uso extensivo desse recurso para separar o código gerado pelo designer do código escrito pelo desenvolvedor para a mesma classe. Por exemplo, no Visual Studio 2002/2003, o código de um formulário é parte integral de uma declaração de classe desse formulário, como:

Public Class Form1
    Inherits System.Windows.Forms.Form

O código de inicialização desse formulário, incluindo qualquer controle colocado no formulário, é gerado pelo Visual Studio. Esse código está no método InitComponent (), o qual, por padrão, é exibido antes do código escrito pelo usuário em uma região chamada "Windows Form Designer generated code" (Código gerado pelo Designer de Formulários do Windows). Normalmente, essa região fica fechada para reduzir a desordem e evitar que você se distraia ao escrever o código. No Visual Studio 2005, esse código evita ainda mais a distração, pois ele é um arquivo completamente diferente, chamado Form1.Designer.vb. Também aqui você poderá ver o conteúdo desse arquivo se desejar. Clique em Show All Files na barra de ferramentas do Solution Explorer, expanda o nó Form1.vb e clique duas vezes em Form1.Designer.vb para exibi-lo no editor de códigos. O arquivo Form1.vb contém apenas o código que você, como desenvolvedor, escreveu para a classe Form1.

No que diz respeito ao código do DataSet e às classes associadas, o uso de classes parciais e arquivos diferentes para separar o código do designer do código do desenvolvedor é ainda mais significativo. Além da organização, essa separação resolve um importante problema que existe ao usar DataSets de tipos no Visual Studio 2002/2003.

Com bastante freqüência, você desejará estender o código gerado automaticamente para o DataSet e suas classes associadas ou fazer inclusões; por exemplo, propriedades adicionais ou código de validação personalizado. Você pode prosseguir e fazer essas inclusões no código gerado. Tudo funciona perfeitamente até você alterar o esquema e precisar gerar o código do DataSet novamente. No Visual Studio 2002/2003, como o código é simplesmente adicionado ao arquivo com o código gerado, todo o código adicional é apagado quando o código é gerado novamente. Graças ao uso de classes parciais, isso não ocorre no Visual Studio 2005. O código recém-gerado substituirá o código do designer existente do arquivo com a extensão .Designer.vb, mas o código escrito pelo desenvolvedor, no arquivo .vb, permanecerá intacto.

Uma maneira de estender a funcionalidade de um DataSet usando classes parciais é adicionar um código de validação personalizado. Essa é uma oportunidade de adicionar alguma lógica específica do aplicativo ao DataSet de tipos gerado. Vamos adicionar validação e inicialização personalizadas ao incluir uma nova linha na tabela de pedidos do NorthwindDataSet. Ao adicionar uma nova linha, queremos verificar se o valor do CEP passado é realmente na cidade informada. Se não for, alteraremos o valor do campo ShipPostalCode para Invalid. Suponhamos que haja uma implementação de uma função que retorne True se o CEP fornecido estiver realmente na cidade informada, da seguinte forma:

Function IsPostalCodeInCity (ByVal PostalCode as string, ByVal City as string) As Boolean

Podemos adicionar essa verificação ao nosso NorthwindDataSet procedendo da seguinte forma:

  • Abra o designer do DataSet selecionando Edit DataSet with Designer na barra de ferramentas da janela Data Sources.

  • Clique duas vezes em uma área vazia do plano de fundo do designer. Isso abrirá o arquivo NorthwindDataSet.vb no editor de códigos.

  • Em vez do código padrão, insira o seguinte código:

Partial Public Class NorthwindDataSet
    Partial Class OrdersDataTable
        Protected Sub ValidateNewRow(ByVal sender As Object, _
               ByVal e As System.Data.DataTableNewRowEventArgs) _
               Handles Me.TableNewRow
            ' Create a strongly typed instance of the row
            ' This helps us avoid code in quotes, 
' eg, e.Row("ShipPostalCode")
            Dim ordersRow As NorthwindDataSet.OrdersRow
            ordersRow = e.Row
            If Not ordersRow.IsShipPostalCodeNull And _
                    Not ordersRow.IsShipCityNull Then
                If Not IsPostalCodeInCity(ordersRow.ShipPostalCode, _
                        ordersRow.ShipCity) Then
                    ' Set the value of the Ship Postal Code
                    ordersRow.ShipPostalCode = "Invalid"
                    ' Typically, changing a users data is a bad user experience
  ' So indicate an error with the ErrorProvider
                    ' We are illustrating both approaches here
                    ordersRow.SetColumnError( _
                        ShipPostalCodeColumn.ColumnName, "Invalid Postal Code")
                Else
                    ' we always need to reset the error when the value is valid
                    ordersRow.SetColumnError( _
                        ShipPostalCodeColumn.ColumnName, String.Empty)
                End If
            End If
        End Sub
    End Class

    Private Shared Function IsPostalCodeInCity(ByVal postalCode As String, _
ByVal city As String) As Boolean
        ' This is a stub, just to check functionality
        If city = "Rio de Janeiro" Then
            Return False
        Else
            Return True
        End If
    End Function
End Class

Observe que todas as classes associadas do DataSet, como o OrdersDataTable, são implementadas como classes aninhadas dentro do DataSet. A declaração de classe parcial mostrada no código anterior reflete essa implementação.

Para ter um erro indicado por um provedor de erros, além de ou em vez de alterar o valor do CEP para Invalid, siga estas etapas:

  1. Arraste um controle Error Provider da Caixa de ferramentas para Form1 e solte-o à direita da caixa de texto Ship Postal Code (CEP de Destino).

  2. Na janela de propriedades de Error Provider, defina a propriedade DataSource como OrdersBindingSource.

Execute o aplicativo e navegue até um registro no qual a cidade seja Rio de Janeiro para ver o código de validação em ação, como mostra a Figura 7.


Figura 7. Controle do provedor de erros

Esse é apenas um exemplo de como você pode estender facilmente a funcionalidade do DataSet de tipos e das classes associadas geradas automaticamente pelo Visual Studio. Talvez também seja conveniente incluir métodos e propriedades adicionais nas classes geradas. Ao desenvolver o aplicativo e usar os DataSets, você encontrará um número muito maior de possibilidades. O ponto essencial a ser lembrado é que no Visual Studio 2005, graças às classes parciais, o código escrito está em um arquivo separado e não é afetado quando as classes DataSet são geradas novamente.

Conclusão

Usar DataSets de tipos gerados pelo Visual Studio 2005 está ainda mais fácil e flexível. O DataSet Designer fornece uma ferramenta mais fácil e mais natural para a definição de DataSets. A nova classe TableAdapter, configurável no DataSet Designer, fornece um mecanismo único e centralizado para manter e executar facilmente várias consultas e comandos diferentes de uma tabela de dados específica. Usar o recurso compilador de classes parciais permite a separação completa entre o código gerado pelo designer e o escrito pelo desenvolvedor, permitindo gerar novamente as classes do DataSet sem afetar qualquer código personalizado já escrito para estender essas classes. Finalmente, as novas classes e os novos mecanismos de ligação de dados do .NET, combinados com as ferramentas oferecidas no Visual Studio 2005, tornam muito mais rápido e fácil o desenvolvimento de aplicativos centrados em dados.

Agradecemos a Steve Lasker, bem como a Alan Griver e a Pablo Castro da Microsoft, pela ajuda na preparação deste artigo.

Sobre o autor

Jackie Goldstein é diretor da Renaissance Computer Systems, especializada em consultoria, treinamento e desenvolvimento com ferramentas e tecnologias da Microsoft. Jackie é um dos diretores regionais da Microsoft, o fundador do Israel Visual Basic User Group e um orador de destaque em eventos internacionais para desenvolvedores, incluindo TechEd, VSLive!, Developer Days e Microsoft PDC. Ele também é autor de Database Access with Visual Basic.NET (Addison-Wesley, ISBN 0-67232-3435) e membro da INETA Speakers Bureau. Em dezembro de 2003, a Microsoft designou Jackie como um .NET Software Legend!

© 2005 Microsoft Corporation. Todos os direitos reservados. Termos de uso.

Page view tracker