WPF - Realizando as operações CRUD em uma fonte de dados XML (C#)


Neste artigo vou mostrar como criar uma aplicação WPF realiza a manutenção de uma fonte de dados XML através das CRUD : incluir, excluir e atualizar.

Este artigo foi adaptado do original em : CRUD Operation on XML

O objetivo é mostrar como usar os recursos da WPF para tratar arquivos XML em uma aplicação simples mas funcional.

Recursos usados:

A nossa fonte de dados será o arquivo XML matricula.xml cuja estrutura podemos ver a seguir:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Alunos>
  <Aluno>
    <Nome>Macoratti</Nome>
    <Curso>Quimica</Curso>
    <Email>macoratti@yahoo.com</Email>
    <Matricula>2011</Idade>
  </Aluno>
  <Aluno>
    <Nome>Marcia Soares</Nome>
    <Curso>Biologia</Curso>
    <Email>marciasoraes@ig.com.br</Email>
    <Matricula>2011</Idade>
  </Aluno>
</Alunos>
O nosso arquivo matricula.xml estará localizado na pasta c:\dados

(você pode colocá-lo em qualquer outro local (como a pasta bin) desde que altere o
caminho do arquivo no código)

Criando o projeto WPF

Abra o Visual C# 2010 Express Edition e crie um novo projeto (File-> New Project) do tipo WPF Application com o nome CRUDXmlDBWPF;

Vamos iniciar definido uma classe com o nome Alunos.cs (Project->Add Class) com o seguinte conteúdo:

namespace CRUDXmlDbWPF
{
    class Alunos
    {
        public string Nome { get; set; }
        public string Curso { get; set; }
        public string Email { get; set; }
        public string Matricula { get; set; }
    }
}

Esta classe representa o nosso domínio e possui 4 propriedades que refletem as tags do arquivo matricula.xml.

Vamos iniciar definindo a interface da aplicação, para isso abra o arquivo Window1.xaml e inclua um controle TabControl a partir da ToolBox definindo 3 TabItens : Ver Todos, Incluir Novo e Atualizar conforme o leiaute da figura abaixo (à direita):

A seguir inclua os seguintes controles em cada TabItem:

Conforme o leiaute das figuras a seguir

No Grid deste TabItem usamos os seguintes controles:
  • DataGrid - ViewDataGrid

No controle DataGrid definimos:

  • IsReadOnly="True"
  • AutoGenerateColumns="False"
No Grid deste TabItem usamos os seguintes controles:
  • TextBox - txtNome
  • TextBox - txtCurso (TextWrapping="Wrap")
  • TextBox - txtEmail (TextWrapping="Wrap")
  • ComboBox - cmbMatricula
  • Button - btnIncluir

No Grid deste TabItem usamos os seguintes controles:

  • TextBox - txtNomeAtualiza
  • TextBox - txtCursoAtualiza (TextWrapping="Wrap")
  • TextBox - txtEmailAtualiza (TextWrapping="Wrap")
  • ComboBox - cmbMatriculaAtualiza
  • Button - btnAtualizar
  • Button - btnDeletar
  • ListBox - lbAlunos
Leiaute  

O código XAML do arquivo Window1.xaml é visto a seguir:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Custom="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Class="CRUDXmlDbWPF.Window1"
    Title="Alunos" Height="400" Width="640" mc:Ignorable="d" ResizeMode="CanMinimize">
    <Grid>
    <Grid.Background>
    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    <GradientStop Color="#FF7C51E1" Offset="1"/>
    <GradientStop Color="White"/>
    </LinearGradientBrush>
    </Grid.Background>
    <TabControl Background="{x:Null}">
    <TabItem Header="Ver Todos">
    <Grid>
                   
<Custom:DataGrid x:Name="ViewDataGrid" IsReadOnly="True" GridLinesVisibility="Horizontal"
                                     AutoGenerateColumns="False" d:LayoutOverrides="Width, Height" Background="{x:Null}">
                        <Custom:DataGrid.Columns>
                            <Custom:DataGridTextColumn Header="Nome" Binding="{Binding Path=Nome}" Width="200"/>
                            <Custom:DataGridTextColumn Header="Curso" Binding="{Binding Path=Curso}" Width="100"/>
                            <Custom:DataGridTextColumn Header="Email" Binding="{Binding Path=Email}" Width="250"/>
                            <Custom:DataGridTextColumn Header="Matricula" Binding="{Binding Path=Matricula}" Width="70"/>
                        </Custom:DataGrid.Columns>
                    </Custom:DataGrid>

                </Grid>
    </TabItem>
    <TabItem Header="Incluir Novo">
    <Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="0.217*"/>
    <ColumnDefinition Width="0.783*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
    <RowDefinition Height="0.083*"/>
    <RowDefinition Height="0.086*"/>
    <RowDefinition Height="0.147*"/>
    <RowDefinition Height="0.146*"/>
    <RowDefinition Height="0.09*"/>
    <RowDefinition Height="0.095*"/>
    <RowDefinition Height="0.353*"/>
    </Grid.RowDefinitions>
   
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Text="Nome :" TextWrapping="Wrap" Margin="0,0,5,0" Grid.Row="1"/>
    <TextBlock HorizontalAlignment="Right" Margin="0,0,5,0" VerticalAlignment="Center" Text="Curso:" TextWrapping="Wrap" Grid.Row="2"/>
    <TextBlock HorizontalAlignment="Right" Margin="0,0,5,0" VerticalAlignment="Center" Grid.Row="3" Text="Email:" TextWrapping="Wrap"/>
    <TextBlock HorizontalAlignment="Right" Margin="0,0,5,0" VerticalAlignment="Center" Text="Matricula :" TextWrapping="Wrap" Grid.Row="4"/>
    <TextBox x:Name="txtNome" Margin="0,0,5,0" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Height="23"/>
    <TextBox x:Name="txtCurso" Margin="0,1,5,1" Grid.Column="1" Grid.Row="2" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
    <TextBox x:Name="txtEmail" Margin="0,1,5,1" Grid.Column="1" Grid.Row="3" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
    <ComboBox x:Name="cmbMatricula" Margin="0" HorizontalAlignment="Left" Width="75" Grid.Column="1" Grid.Row="4" Height="20" VerticalAlignment="Center"/>
    <Button x:Name="btnIncluir" Click="btnIncluir_Click"
                            HorizontalAlignment="Left" Margin="0" VerticalAlignment="Center" Width="100" Height="25" Content="Incluir " Grid.Column="1" Grid.Row="5"/>

    </Grid>
    </TabItem>
    <TabItem Header="Atualizar">
    <Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="109"/>
    <ColumnDefinition Width="495"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
    <RowDefinition Height="0.083*"/>
    <RowDefinition Height="0.086*"/>
    <RowDefinition Height="0.147*"/>
    <RowDefinition Height="0.146*"/>
    <RowDefinition Height="0.09*"/>
    <RowDefinition Height="0.095*"/>
    <RowDefinition Height="0.353*"/>
    </Grid.RowDefinitions>
   
<TextBlock Margin="36,0,5,0" VerticalAlignment="Center" Grid.Row="1" Text="Nome:" TextWrapping="Wrap" Grid.Column="1" d:LayoutOverrides="Width" Width="68" />
    <TextBlock Margin="37,0,5,0" VerticalAlignment="Center" Text="Curso:" TextWrapping="Wrap" Grid.Row="2" Grid.Column="1" d:LayoutOverrides="Width" Width="67" />
    <TextBlock Margin="6,0,5,0" VerticalAlignment="Center" Grid.Row="3" Text="Email:" TextWrapping="Wrap" Grid.Column="1" d:LayoutOverrides="Width" Width="36" />
    <TextBlock Margin="36,5,5,8" VerticalAlignment="Center" Text="Matrícula" TextWrapping="Wrap" Grid.Row="4" Grid.Column="1" d:LayoutOverrides="Width" Width="68" Height="17" />
    <TextBox x:Name="txtNomeAtualiza" Margin="0,0,5,0" VerticalAlignment="Center" Height="23" Grid.Column="2" Grid.Row="1"/>
    <TextBox x:Name="txtCursoAtualiza" Margin="0,1,5,1" Grid.Column="2" Grid.Row="2" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
    <TextBox x:Name="txtEmailAtualiza" Margin="0,1,5,1" Grid.Column="2" Grid.Row="3" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
    <ComboBox x:Name="cmbMatriculaAtualiza" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Center" Width="75" Height="20" Grid.Column="2" Grid.Row="4"/>
    <Button x:Name="btnAtualizar" Click="btnAtualizar_Click"
                            HorizontalAlignment="Left" Margin="0" VerticalAlignment="Center" Width="100" Height="25" Content="Atualizar" Grid.Column="2" Grid.Row="5"/>
    <Button x:Name="btnDeletar" Click="btnDeletar_Click"
                            Margin="108,0,188.819,0" VerticalAlignment="Center" Height="25" Content="Deletar" Grid.Column="2" Grid.Row="5" Width="100"/>

                    <
ListBox x:Name="lbAlunos" SelectionChanged="lbAlunos_SelectionChanged"
                             Margin="0,8" Grid.RowSpan="7">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Nome}"/>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>

                </Grid>
    </TabItem>
    </TabControl>
       
    </Grid>
</Window>

Definindo o código da aplicação

No arquivo Window1.xaml.cs vamos definir o código usado pela nossa aplicação.

Iniciamos com a declaração dos namespaces das classes usadas pela aplicação no início do arquivo:

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;
using System.Collections.ObjectModel;
using System.IO;
using System.Xml;

A seguir, logo após a declaração da janela Window definimos as variáveis globais usadas pela aplicação:

ObservableCollection<Alunos> selectedList = new ObservableCollection<Alunos>();
List<Alunos> listaAlunos;
List<string> listaAnos;
Alunos alunosDel;
string caminhoArquivoXML = @"c:\dados\Matricula.xml";

A classe ObservableCollection<T> que fica no namespace System.Collections.ObjectModel, que fornece notificações, quando a coleção é modificada, incluindo, alterando ou mesmo excluindo algum item.

Logo a seguir temos a inicialização das variáveis , a definição dos dados da combobox e a carga dos dados:

 public Window1()
        {
            InitializeComponent();
            btnAtualizar.IsEnabled = false;
            btnDeletar.IsEnabled = false;
            listaAnos = new List<string> { "2005", "2006", "2007", "2008", "2009", "2010", "2011" };
            cmbMatricula.ItemsSource = listaAnos;
            cmbMatriculaAtualiza.ItemsSource = listaAnos;
            carregaDados();
        }

A rotina carregaDados() tem o seguinte código:

        void carregaDados()
        {
            try
            {
                XDocument doc = XDocument.Load(caminhoArquivoXML);
             
  listaAlunos = (from aluno in doc.Descendants("Aluno")
                                    orderby aluno.Element("Nome").Value
                                    select new Alunos
                                   {
                                       Nome = aluno.Element("Nome").Value,
                                       Curso = aluno.Element("Curso").Value,
                                       Email = aluno.Element("Email").Value,
                                       Matricula = aluno.Element("Matricula").Value,
                                   }).ToList();

                lbAlunos.ItemsSource = listaAlunos;
                ViewDataGrid.ItemsSource = listaAlunos;
            }
            catch
            {
                MessageBox.Show("Erro ao acessar dados");
            }
        }

Neste código usamos uma consulta para obter os valores do arquivo XML e exibi-los no controle ListBox e no DataGrid via propriedade ItemsSource.

No evento Click do botão Incluir temos o código que inclui uma informação no arquivo XML:

   private void btnIncluir_Click(object sender, RoutedEventArgs e)
        {
            string nome = txtNome.Text.Trim();
            string matricula;
            if (cmbMatricula.SelectedIndex != -1)
            {
                matricula = cmbMatricula.SelectedValue.ToString();
            }
            else
            {
                matricula = "2010";
            }
            string curso = txtCurso.Text.Trim();
            string email = txtEmail.Text.Trim();

            bool isUpdate = false;

            if (nome.Equals(string.Empty))
                nome = "não informado";
            if (matricula.Equals(string.Empty))
                matricula = "2011";
            if (curso.Equals(string.Empty))
                curso = "indefinido";
            if (email.Equals(string.Empty))
                email = "não informado";

     
      Alunos novoAluno = new Alunos
            {
                Nome = nome,
                Curso = curso,
                Email = email,
                Matricula = matricula,
            };


            WriteToXmlFile(novoAluno, isUpdate);
            txtNome.Text = string.Empty;
            txtCurso.Text = string.Empty;
            txtEmail.Text = string.Empty;
            cmbMatricula.SelectedIndex = -1;
            carregaDados();
        }

Neste código atribuímos os valores para as propriedades do objeto novoAluno que é uma instância da classe Alunos e chamamos o método WriteToXmlFIle passando o objeto novoAluno para ser persistido no arquivo XML e em seguida limpamos os controles e carregamos os dados novamente.

No evento Click do botão Atualizar temos o código que permite alterar uma informação no arquivo XML:

      private void btnAtualizar_Click(object sender, RoutedEventArgs e)
        {
            if (lbAlunos.SelectedIndex != -1)
            {
                string nome = txtNomeAtualiza.Text;
                string curso = txtCursoAtualiza.Text;
                string email = txtEmailAtualiza.Text;
                string matricula = cmbMatriculaAtualiza.SelectedValue.ToString();

                bool isUpdate = true;

                if (nome.Equals(string.Empty))
                    nome = "não informado";
                if (matricula.Equals(string.Empty))
                    matricula = "2009";
                if (curso.Equals(string.Empty))
                    curso = "indefinido";
                if (email.Equals(string.Empty))
                    email = "não informado";

         
      Alunos atualizaAluno = new Alunos
                {
                    Nome = nome,
                    Curso = curso,
                    Email = email,
                    Matricula = matricula,
                };


                WriteToXmlFile(atualizaAluno, isUpdate);
                lbAlunos.SelectedIndex = -1;
                btnAtualizar.IsEnabled = false;
                btnDeletar.IsEnabled = false;
                carregaDados();
            }
        }

O procedimento é o mesmo do código anterior, a diferença é que definimos a variável IsUpdate como false para realizar uma atualização nos dados.

No evento Click do botão Deletar temos o código que exclui uma informação do arquivo XML:

     private void btnDeletar_Click(object sender, RoutedEventArgs e)
        {
            if (alunosDel != null)
            {
                if (System.Windows.MessageBox.Show("Deseja excluir este aluno ?", "Confirmar Exclusão",
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                {
                    XDocument doc = XDocument.Load(caminhoArquivoXML);

                  
 foreach (var item in doc.Descendants("Aluno"))
                    {
                        if (item.Element("Nome").Value == alunosDel.Nome)
                        {
                            ((XElement)item.Element("Nome")).Parent.Remove();
                            doc.Save(caminhoArquivoXML);
                            break;
                        }
                    }

                    carregaDados();
                    alunosDel = null;
                    lbAlunos.SelectedIndex = -1;
                    btnDeletar.IsEnabled = false;
                }
                else
                {
                    alunosDel = null;
                    lbAlunos.SelectedIndex = -1;
                    btnDeletar.IsEnabled = false;
                }
            }
        }

A rotina WriteToXmlFile é quem realiza a persistência das operações no arquivo XML e tem o seguinte código:

   void WriteToXmlFile(Alunos alunos, bool isUpdate)
        {
            if (!isUpdate)
            {
                FileStream fs = new FileStream(caminhoArquivoXML, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(fs);
                fs.Close();

                XmlElement novoAluno = xmlDoc.CreateElement("Aluno");
                XmlElement nome = xmlDoc.CreateElement("Nome");
                nome.InnerText = alunos.Nome;
                novoAluno.AppendChild(nome);

                XmlElement curso = xmlDoc.CreateElement("Curso");
                curso.InnerText = alunos.Curso;
                novoAluno.AppendChild(curso);

                XmlElement email = xmlDoc.CreateElement("Email");
                email.InnerText = alunos.Email;
                novoAluno.AppendChild(email);

                XmlElement matricula = xmlDoc.CreateElement("Matricula");
                matricula.InnerText = alunos.Matricula;
                novoAluno.AppendChild(matricula);

                xmlDoc.DocumentElement.InsertAfter(novoAluno,xmlDoc.DocumentElement.LastChild);

                FileStream fsxml = new FileStream(caminhoArquivoXML, FileMode.Truncate,
                                      FileAccess.Write,
                                      FileShare.ReadWrite);


                xmlDoc.Save(fsxml);
                fsxml.Close();
            }
            else
            {
                XDocument doc = XDocument.Load(caminhoArquivoXML);

              
 foreach (var item in doc.Descendants("Aluno"))
                {
                    if (item.Element("Nome").Value == alunosDel.Nome)
                    {
                        item.Element("Nome").SetValue(alunos.Nome);
                        item.Element("Curso").SetValue(alunos.Curso);
                        item.Element("Email").SetValue(alunos.Email);
                        item.Element("Matricula").SetValue(alunos.Matricula);
                        doc.Save(caminhoArquivoXML);
                        break;
                    }
                }

            }
        }

O evento SelectionChanged do controle ListBox é exibido a seguir:

  private void lbAlunos_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            selectedList.Clear();
            if (this.lbAlunos.SelectedItem is Alunos)
            {
                selectedList.Add(((Alunos)this.lbAlunos.SelectedItem));
                alunosDel = (Alunos)this.lbAlunos.SelectedItem;
            }
            foreach (var item in selectedList)
            {
                txtNomeAtualiza.Text = item.Nome;
                txtCursoAtualiza.Text = item.Curso;
                txtEmailAtualiza.Text = item.Email;
                cmbMatriculaAtualiza.SelectedValue = item.Matricula;
            }
            btnAtualizar.IsEnabled = true;
            btnDeletar.IsEnabled = true;
        }

Ele limpa a lista e a seguir preenche a lista com os dados do aluno.

Executando o projeto podemos ver nas figuras abaixo cada uma das opções sendo usada na aplicação:

1- Exibição dos dados no DataGrid:

2- Inclusão de dados

3- Alteração de dados

E assim vimos que a WPF possui poderosos recursos para tratar arquivo XML usando o recurso do DataBinding.

Pegue o projeto completo aqui: CRUDXmlDbWPF.zip

Eu sei é apenas WPF, mas eu gosto...

"Passará o céu e a terra, mas as minhas palavras jamais passarão." (Mateus 24:35)

Referências:

José Carlos Macoratti