C# - Operações Básicas com XML


Neste artigo eu vou mostrar as operações básicas usando arquivos XML em aplicações Windows Forms usando a linguagem C#.

Conceitos básicos - Teoria

O esquema de um DataSet (suas tabelas, colunas, relações e restrições) pode ser definido por meio de programação, criada pelos métodos Fill ou FillSchema de um DataAdapter, ou carregado a partir de um documento XML.

Para carregar as informações do esquema do DataSet a partir de um documento XML, você pode usar tanto o método ReadXmlSchema como o método InferXmlSchema do DataSet.

Para carregar o esquema de um DataSet a partir de um documento XML, sem carregar todos os dados, você pode usar o método ReadXmlSchema do DataSet. O método ReadXmlSchema cria um esquema DataSet definido usando o esquema - XML Schema Definition(XSD).

O método ReadXmlSchema recebe um único argumento de um nome de arquivo, um stream, ou um XmlReader que contém o documento XML a ser carregado. O documento XML pode conter apenas o esquema, ou pode conter o esquema inline com elementos XML que contêm dados.

O conteúdo de um DataSet pode ser criado a partir de um documento ou stream XML.

Para preencher um DataSet com dados de XML, usamos o método ReadXml do objeto DataSet. O método ReadXml vai ler um arquivo, um stream, ou um XmlReader, e toma como argumentos a fonte do XML mais um argumento opcional XmlReadMode.

O método ReadXml lê o conteúdo do documento ou stream XML e carrega o DataSet com dados. Ele também irá criar o esquema relacional do DataSet dependendo do XmlReadMode especificado e se um esquema relacional já existe ou não.

Com base neste conhecimento vamos mostrar alguns exemplos de aplicações Windows Forms usando a linguagem C#.

Nos exemplos usados neste artigo eu vou utilizar como fonte de dados um arquivo XML e um arquivo XSD.

Obs: XML Schema é uma alternativa ao DTD baseada em XML. Um esquema XML descreve a estrutura de um documento XML. A linguagem XML Schema também é chamada de XML Schema Definition (XSD).

Estes arquivos estão localizados na pasta bin/Debug da aplicação para facilitar o acesso e distribuição em tempo de testes.

Apenas para clarear o entendimento exibo a seguir uma parte do conteúdo de cada arquivo :

<?xml version="1.0" standalone="yes"?>
<NewDataSet>
  <Products>
    <ProductID>355</ProductID>
    <CategoryID>16</CategoryID>
    <ModelNumber>RU007</ModelNumber>
    <ModelName>Rain Racer 2000</ModelName>
    <ProductImage>image.gif</ProductImage>
    <UnitCost>1499.99</UnitCost>
    <Description>Looks like an ordinary bumbershoot, but don't be fooled! Simply 
    place Rain Racer's tip on the ground and press the release latch. Within seconds, 
   this ordinary rain umbrella converts into a two-wheeled gas-powered mini-scooter. 
   Goes from 0 to 60 in 7.5 seconds - even in a driving rain! Comes in black, blue, 
   and candy-apple red.</Description>
  </Products>
  <Products>
    <ProductID>356</ProductID>
    <CategoryID>20</CategoryID>
  ..........
  ..........
  <Categories>
    <CategoryID>19</CategoryID>
    <CategoryName>Tools</CategoryName>
  </Categories>
  <Categories>
    <CategoryID>20</CategoryID>
    <CategoryName>General</CategoryName>
  </Categories>
</NewDataSet>
Arquivo store.xml
<?xml version="1.0" standalone="yes"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="NewDataSet" msdata:IsDataSet="true">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element name="Products">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="ProductID" msdata:ReadOnly="true" msdata:AutoIncrement="true" type="xs:int" />
              <xs:element name="CategoryID" type="xs:int" />
              <xs:element name="ModelNumber" minOccurs="0">
                <xs:simpleType>
                  <xs:restriction base="xs:string">
                    <xs:maxLength value="50" />
                  </xs:restriction>
                </xs:simpleType>
              </xs:element>
                ...........
                ...........  
    <xs:unique name="Categories_Constraint1" msdata:ConstraintName="Constraint1" msdata:PrimaryKey="true">
      <xs:selector xpath=".//Categories" />
      <xs:field xpath="CategoryID" />
    </xs:unique>
  </xs:element>
</xs:schema>
Arquivo store.xsd

Arquivos XSD (XML Schema Definition) são usados para descrever o “formato/padrão” que um arquivo XML deve seguir, ou seja, ele tem que indicar quais nodes (<node1><subnode1/></node1>) ele pode conter, quais subnodes e atributos esses nodes podem ter, e muito mais.

Indica o tipo dos valores que esses nodes e atributos (<node1 atributo1=’abc’/>) podem armazenar, o tamanho dos dados caso se aplique (string de 10 caracteres), se um determinado node é obrigatório ou não, quais possíveis valores uma enumeração pode assumir, etc. Ou seja, define toda a estrutura do arquivo XML ao qual ele é aplicado.

Da Teoria para a Prática

Vamos agora à parte prática usando os conceitos apresentados.

Em todos os exemplos irei usar o Visual C# 2010 Express Edition criando uma solução (File-> New Project) com o nome AcessoXML;

1- Preenchendo um ListBox com a fonte de dados XML

Vamos preencher um controle ListBox com as informações XML exibindo a informação ModelName da tabela Products.

Altere o nome do formulário form1.cs para frmListBox.cs e inclua no formulário, a partir da ToolBox, um controle ListBox (name=lstDadosXML) e um controle Button (name= btnPreencherListBox) conforme o leiaute da figura 1.0 abaixo:

Figura 1.0

Figura 2.0

No evento Click do botão de comando btnPreencherListBox digite o código a seguir:

 private void btnPreencherListBox_Click(object sender, EventArgs e)
 {
            DataSet dsStore = new DataSet();
            dsStore.ReadXmlSchema(Application.StartupPath + "\\store.xsd");
            dsStore.ReadXml(Application.StartupPath + "\\store.xml");

            lstDadosXML.DataSource = dsStore.Tables["Products"];
            lstDadosXML.DisplayMember = "ModelName";
 }

No código acima estamos definindo um novo DataSet e usando os métodos ReadXmlSchema e ReadXml para ler as informações da tabela Products e exibir o nome da tag ModelName;

O resultado obtido é o da figura 2.0 acima.

2- Preenchendo dois ListBoxes com a fonte de dados XML e visualização Master Detail

Vamos agora preencher dois controles ListBox onde em um vamos exibir as informações da tabela Categorias (Categories) e no outro as informações da tabela Produtos(Products) relacionados;

Vamos incluir um novo formulário no projeto, menu Project-> Add Windows Forms com onome frmMestreDetalhes.cs;

A seguir vamos incluir dois controles ListBox no formulário um para exibir as categorias (name=lstCategorias) e outro para exibir os produtos relacionados (name=lstProdutos);

Vamos incluir também um controle Button (name=btnCarregarXML) conforme o leiaute abaixo:

No início do formulário vamos declarar as variáveis para a BindingManagerBase e para o DataSet que iremos usar para preencher os controles;

private BindingManagerBase categoryBinding;
private DataSet dsStore = new DataSet();

Cada objeto Binding se comunica com um CurrencyManager ou PropertyManager. As classes CurrencyManager e PropertyManager são derivadas da classe base BindingManagerBase.

O objetivo da classe BindingManagerBase é manter a concorrência entre a fonte de dados e o controle. Das duas classes a classe CurrencyManager é usada quando a fonte de dados implementa a interface IList ( DataView, DataSet, ArrayList,etc.)

Após isso vamos incluir o código abaixo no evento Click do botão de comando - btnCarregarXML:

private void btnCarregarXML_Click(object sender, EventArgs e)
{
            //acessa os arquivos XML e XSD na basta bin\Debug
            dsStore.ReadXmlSchema(Application.StartupPath + "\\store.xsd");
            dsStore.ReadXml(Application.StartupPath + "\\store.xml");

            //carrega os dados da tabela Categories ,tag CategoryName
            lstCategorias.DataSource = dsStore.Tables["Categories"];
            lstCategorias.DisplayMember = "CategoryName";

            //carrega os dados da tabela Products ,tag ModelName
            lstProdutos.DataSource = dsStore.Tables["Products"];
            lstProdutos.DisplayMember = "ModelName";

            categoryBinding = this.BindingContext[dsStore.Tables["Categories"]];

            //define o delegate
            categoryBinding.PositionChanged += new EventHandler(Binding_PositionChanged);

            // Invoca o método para atualizar a tabela filha na inicialização
            Binding_PositionChanged(null, null);
}

A seguir vamos definir o código do evento delegate que irá filtrar os produtos pela categoria selecionada:

 private void Binding_PositionChanged(object sender, System.EventArgs e)
        {
            string filtro;
            DataRow selectedRow;

            // Encontra a linha da categoria atual
            selectedRow = dsStore.Tables["Categories"].Rows[categoryBinding.Position];

            // Cria um filtro usando o codigo da categoria -  CategoryID.
            filtro = "CategoryID='" + selectedRow["CategoryID"].ToString() + "'";

            // Modifica a view na tabela product
            dsStore.Tables["Products"].DefaultView.RowFilter = filtro;
        }

Executando o projeto temos o resultado abaixo:

Obs: Para executar o formulário frmMestreDetalhes altere no programa Program.cs linha que chama o formulário para: Application.Run(new frmMestreDetalhes());

3- DataBinding com Edição de dados XML

Vamos agora mostrar como podemos realizar a vinculação dos dados XML em controles de formulário Windows Forms e permitir a edição de dados de forma a alterar os dados das fontes XML.

No menu Project clique em Add Windows Forms e inclua um novo formulário Windows com o nome igual a frmDataBindingEditavel.cs;

A seguir vamos incluir neste formulário os seguintes controles:

- 1 Controle Combobox - name = cboModeloNome;
- 1 Controle GroupBox - usado apenas para agrupar os controles TextBox;
- 4 Controles TextBox : txtModeloNome, txtModeloNumero, txtCustoUnitario e txtDescricao
- 1 Controle Button - btnCarregarXML

O leiaute do formulário deve estar conforme a figura abaixo:

No evento Click do botão de comando btnCarregarXML inclua o código a seguir:

 private void btnCarregarXML_Click(object sender, EventArgs e)
 {
            DataSet dsStore = new DataSet();
            dsStore.ReadXmlSchema(Application.StartupPath + "\\store.xsd");
            dsStore.ReadXml(Application.StartupPath + "\\store.xml");

            cboModeloNome.DataSource = dsStore.Tables["Products"];
            cboModeloNome.DisplayMember = "ModelName";

            txtModeloNome.DataBindings.Add("Text", dsStore.Tables["Products"], "ModelName");
            txtModeloNumero.DataBindings.Add("Text", dsStore.Tables["Products"], "ModelNumber");
            txtCustoUnitario.DataBindings.Add("Text", dsStore.Tables["Products"], "UnitCost");
            txtDescricao.DataBindings.Add("Text", dsStore.Tables["Products"], "Description");
 }

O código vincula os campos Texto("Text") usando a minha fonte de dados (dsStore) a cada campo da tabela que desejamos exibir no formulário.

Executando o projeto teremos o resultado abaixo:

Obs: Para executar o formulário frmDataBindingEditavel.cs altere no programa Program.cs linha que chama o formulário para: Application.Run(new frmDataBindingEditavel.cs());

Você notará que poderemos selecionar um registro e os dados exibidos nos controles serão sincronizados.

Qualquer alteração de dados feita no formulário será propagada para o XML.

4- Múltiplos DataBinding com Navegação

Agora vamos acessar a fonte de dados XML e realizar múltiplos binding e permitir a navegação.

No menu Project clique em Add Windows Forms e inclua um novo formulário Windows com o nome igual a frmNavegacao.cs;

Inclua os seguintes controles no formulário:

O leiaute do formulário é mostrado a seguir:

Vamos declarar a variável do tipo BindingManagerBase no início do formulário:

private BindingManagerBase storeBinding;

Agora no evento Load do formulário vamos incluir o código para acessar, carregar e vincular os dados XML aos controles do formulário:

private void frmNavegacao_Load(object sender, EventArgs e)
        {
            DataSet dsStore = new DataSet();

            dsStore.ReadXmlSchema(Application.StartupPath + "\\store.xsd");
            dsStore.ReadXml(Application.StartupPath + "\\store.xml");

            cboNomeModelo.DataSource = dsStore.Tables["Products"];
            cboNomeModelo.DisplayMember = "ModelName";

            lblNumeroModelo.DataBindings.Add("Text",dsStore.Tables["Products"], "ModelNumber");
            lblCustoUnitario.DataBindings.Add("Text",dsStore.Tables["Products"], "UnitCost");
            lblDescricao.DataBindings.Add("Text", dsStore.Tables["Products"], "Description");

            storeBinding = this.BindingContext[dsStore.Tables["Products"]];
            storeBinding.PositionChanged += new EventHandler(Binding_PositionChanged);
        }

Precisamos defnir o evento Binding_PositionChanged que controla a navegação conforme abaixo:

 private void Binding_PositionChanged(object sender, System.EventArgs e)
 {
            if (storeBinding.Position == storeBinding.Count - 1)
            {
                btnProximo.Enabled = false;
            }
            else
            {
                btnProximo.Enabled = true;
            }

            if (storeBinding.Position == 0)
            {
                btnAnterior.Enabled = false;
            }
            else
            {
                btnAnterior.Enabled = true;
            }
 }

Para concluir basta incluir o código nos botões de comando para navegar pelos dados:

1- Botão - btnProximo

 private void btnProximo_Click(object sender, EventArgs e)
  {
            storeBinding.Position++;
 }

2- Botão - btnAnterior

  private void btnAnterior_Click(object sender, EventArgs e)
  {
            storeBinding.Position--;
  }

Executando o projeto iremos obter o resultado a seguir:

Dessa forma temos a vinculação dos dados XML aos controles e permitimos também a navegação pelos registros;

Com isso mostrei como podemos acessar uma fonte de dados XML e realizar algumas tarefas com formulários Windows Forms usando o C#.

Pegue o projeto completo aqui: AcessoXML.zip

João 2:13 Estando próxima a páscoa dos judeus, Jesus subiu a Jerusalém.
João 2:14
E achou no templo os que vendiam bois, ovelhas e pombas, e também os cambistas ali sentados;
João 2:15
e tendo feito um azorrague de cordas, lançou todos fora do templo, bem como as ovelhas e os bois; e espalhou o dinheiro dos cambistas, e virou-lhes as mesas;
João 2:16
e disse aos que vendiam as pombas: Tirai daqui estas coisas; não façais da casa de meu Pai casa de negócio.

João 2:17
Lembraram-se então os seus discípulos de que está escrito: O zelo da tua casa me devorará.

Referências:


José Carlos Macoratti