WPF - Controle de usuários com ADO .NET


O objetivo deste artigo é mostrar como criar uma aplicação WPF básica para gerenciar um cadastro de usuários com login e senha usando os recursos da ADO .NET.

Nossa aplicação deverá acessar um banco de dados SQL Server 2008 Express chamado Usuarios, criado no SQL Server Management Studio, que possui a tabela Userdata com a seguinte estrutura:

Estrutura da tabela Userdata :
  • ID
  • usuario
  • senha

onde ID é uma chave primária do tipo Identity.

O foco é gerenciar o cadastro de usuários realizando as operações CRUD - inclusão, atualização e exclusão de usuários.

Vamos abrir o Visual C# 2010 Express Edition e criar um novo projeto do tipo WPF Application: File -> New Project -> WPF Application;

Informe o nome ControlaUsuarios e clique em OK;

Vamos incluir no projeto um arquivo de configuração para que possamos armazenar a string de conexão com o banco de dados SQL Server.

No menu Project -> Add New Item , selecione o template Application Configuration File, aceite o nome App.Config e clique em Add;

Isso irá criar o arquivo App.Config no projeto. Agora inclua no arquivo o código abaixo que define a string de conexão com o banco de dados usado no projeto:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="conexaoSQLServer"
     providerName="System.Data.SqlClient"
     connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Usuarios.mdf;Integrated Security=True;User Instance=True;" />
  </connectionStrings>
</configuration>

Note que definimos o nome da seção : conexaoSQLServer , o provedor usado: System.Data.SqlClient e a string de conexão usada.

Selecione o arquivo MainWindow1.xaml e a partir da ToolBox vamos incluir nesta janela os seguintes controles:

Defina o leiaute do formulário conforme a figura abaixo:

O código XAML gerado será o seguinte:

<Window x:Class="DatabaseApplication.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Controle de Usuários" Height="373" Width="522" Loaded="Window_Loaded" Background="White">
    <Grid Height="336" Width="503" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="141*" />
            <ColumnDefinition Width="356*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="136*" />
            <RowDefinition Height="198*" />
            <RowDefinition Height="2*" />
        </Grid.RowDefinitions>
        <ListView Margin="8,9,68,125" Name="lstvUsuarios" ItemsSource="{Binding}" MinWidth="350" MinHeight="100" Grid.ColumnSpan="2" Grid.RowSpan="2" Background="#BFF3D9D2">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Path=ID}"></GridViewColumn>
                    <GridViewColumn Header="Usuário" DisplayMemberBinding="{Binding Path=usuario}"></GridViewColumn>
                    <GridViewColumn Header="Senha" DisplayMemberBinding="{Binding Path=senha}"></GridViewColumn>
                 </GridView>
            </ListView.View>
        </ListView>
        <TextBox Margin="92,81,145,96" Name="txtUsuario" DataContext="{Binding ElementName=lstvUsuarios,Path=SelectedItem}" _ 
Text="{Binding Path=usuario}" Grid.ColumnSpan="2" Grid.Row="1" Background="#FFEFFAC3" />
        <TextBox Height="23" Margin="92,0,145,65" Name="txtSenha" VerticalAlignment="Bottom" DataContext="{Binding ElementName=lstvUsuarios,Path=SelectedItem}" _ 
Text="{Binding Path=senha}" Grid.ColumnSpan="2" Grid.Row="1" Background="#7DEBE88D" CharacterCasing="Normal" />
        <Label Margin="16,81,53,92" Name="label1" Grid.Row="1" Content="Usuário :"></Label>
        <Label Height="30" Margin="16,0,53,56" Name="label2" VerticalAlignment="Bottom" Grid.Row="1" Content="Senha :"></Label>
        <Button Height="26" Margin="16,0,51,23.5" Name="btnIncluir" VerticalAlignment="Bottom" Click="btnIncluir_Click" Grid.Row="1" Content="Incluir"></Button>
        <Button Height="26" Margin="102,0,0,23.5" Name="btnAtualizar" VerticalAlignment="Bottom" Click="btnAtualizar_Click" HorizontalAlignment="Left" Width="75" _ 
Grid.ColumnSpan="2" Grid.Row="1" Content="Atualizar"></Button>
        <Button Height="26" Margin="51,0,0,23.5" Name="btnDeletar" VerticalAlignment="Bottom" Click="btnDeletar_Click" Grid.Column="1" HorizontalAlignment="Left"  _ 
Width="75" Grid.Row="1" Content="Deletar"></Button>
        <Button Height="27.5" Margin="136,0,145,22.75" Name="btnLimpar" VerticalAlignment="Bottom" Click="btnLimpar_Click" Grid.Column="1" Grid.Row="1" Content="Limpar"></Button>
    </Grid>
</Window>

Observe que estou usando os recursos do DataBinding do WPF para vincular os dados da tabela nos controles do formulário.

Essa é a interface da nossa aplicação e através dela iremos realizar o gerenciamento dos usuários existentes na tabela Userdata;

- Ao carregar o projeto o controle ListView deverá exibir os usuários cadastrados;
- Ao selecionar uma linha do controle ListView as informações de nome de usuário e senha deverão ser exibidos nas caixas de texto do formulário;
- No evento Click de cada um dos botões teremos o código que irá realizar as operações CRUD para manutenção dos dados;

Para tornar nossa pequena aplicação mais aderente as boas práticas iremos criar uma classe chamada Acesso que será responsável por realizar as operações CRUD usando os recursos da ADO .NET;

Dessa forma teremos uma classe responsável pelo acesso e persistência dos dados na aplicação separando assim as responsabilidades de acesso a dados da intereface.

No menu Project, selecione Add Class , informe o nome Acesso.cs e clique em Add;

As declarações de uso de namespaces definidos na classe são:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

O namespace adotado para esta classe será: DatabaseApplication

Devemos também incluir uma referência ao namespace System.Configuration no projeto.

No menu Project -> Add Reference, selecione a Aba .NET na janela Add Reference e selecione : System.Configuration e clique em OK;

Vamos agora definir os seguintes métodos nesta classe:

A seguir irei descrever cada um dos métodos :

1- getConexaoSQLServer() - obtém e retorna um a conexão com o SQL Server a partir do arquivo App.Config.

/// <summary>
/// Obtem uma conexão com o banco de dados
/// </summary>
/// <returns></returns>

public static SqlConnection getConexaoSQLServer()
{
    string strCon = GetConnectionStringByNome("conexaoSQLServer");

    if (strCon == null)
          throw new Exception("String de conexão não localizada.");

    SqlConnection con = new SqlConnection(strCon);
    return con;
}

2- closeConexaoSQLServer() - Fecha uma conexão com o SQL server.

/// <summary>
/// Fecha a conexao
/// </summary>
/// <param name="conexao"></param>
public static void closeConexaoSQLServer(SqlConnection conexao)
{
        if (conexao.State == ConnectionState.Open) 
                conexao.Close();
}

3- Insert(usuario,senha) - Inclui um novo usuário e senha.

       /// <summary>
        /// inclui um novo usuario
        /// </summary>
        /// <param name="usuario"></param>
        /// <param name="senha"></param>
        public static void Insert(string usuario, string senha)
        {
            if (!verificaUsuario(usuario))
            {
                throw new Exception("Usuário ja existe.");
            }

            SqlConnection con = Acesso.getConexaoSQLServer();
            try
            {
                con.Open();
                SqlCommand comm = new SqlCommand("insert into userdata(usuario,senha) values(@usuario,@senha)", con);
                comm.Parameters.AddWithValue("@usuario", usuario);
                comm.Parameters.AddWithValue("@senha", GerarHash(senha));
                comm.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                closeConexaoSQLServer(con);
            }
        }

4- verificaUsuario(user) - Verifica se o nome do usuário informado já esta cadastrado.

     /// <summary>
        /// Verifica se um usuário ja esta cadastrado
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        private static bool verificaUsuario(string user)
        {
            SqlDataReader dr = null;
            SqlConnection con = getConexaoSQLServer();
            string sql = "Select * from userdata where usuario='" + user +"'";
            try
            {
                con.Open();
                SqlCommand cmd = new SqlCommand( sql, con);
                dr = cmd.ExecuteReader();
                if (dr.HasRows)
                {
                    return false;
                }
                else
                {
                    return true;
                }
                
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

5- Update(id,usuario,senha) - Atualiza os dados de um usuário.

      /// <summary>
        /// atualiza um registro selecionado
        /// </summary>
        /// <param name="id"></param>
        /// <param name="usuario"></param>
        /// <param name="senha"></param>
        public static void Update(int id,string usuario, string senha)
        {
            SqlConnection con = getConexaoSQLServer();
            try
            {
                con.Open();
                SqlCommand comm = new SqlCommand("update userdata  set usuario=@usuario,senha=@senha where id=@id", con);
                comm.Parameters.AddWithValue("@id", id);
                comm.Parameters.AddWithValue("@usuario", usuario);
                comm.Parameters.AddWithValue("@senha", GerarHash(senha));
                comm.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                closeConexaoSQLServer(con);
            }
        }

6- Delete(id) - Exclui um usuário existente com base no id do usuário.

        /// <summary>
        /// deleta um registro selecionado
        /// </summary>
        /// <param name="id"></param>
        public static void Delete(int id)
        {
            SqlConnection con = getConexaoSQLServer();
            try
            {
                con.Open();
                SqlCommand comm = new SqlCommand("delete from userdata where id=@id", con);
                comm.Parameters.AddWithValue("@id", id);
                comm.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                closeConexaoSQLServer(con);
            }
        }

7- getTabela() - Retorna um DataTable contendo todos os usuários cadastrados.

        /// <summary>
        /// retorna um datatable com todos os usuarios
        /// </summary>
        /// <returns></returns>
        public static DataTable getTabela()
        {
            SqlConnection con = getConexaoSQLServer();
            try
            {
                con.Open();
                SqlCommand comm = new SqlCommand("Select * from userdata", con);
                DataTable dt = new DataTable();
                SqlDataAdapter da = new SqlDataAdapter(comm);
                da.Fill(dt);
                return dt;
            }
            catch(Exception ex)
            {
                throw ex;
            }
            finally
            {
                closeConexaoSQLServer(con);
            }
        }

8- GeraHash() - Gera o hash para a senha do usuário.

        /// <summary>
        /// gera o hash
        /// </summary>
        /// <param name="Valor"></param>
        /// <returns></returns>
        public static string GerarHash(string Valor)
        {
            System.Security.Cryptography.SHA1Managed Sha = new System.Security.Cryptography.SHA1Managed();
            Sha.ComputeHash(System.Text.Encoding.Default.GetBytes(Valor));
            return Convert.ToBase64String(Sha.Hash);
        }

Definindo o código da Interface

A seguir veremos o código dos eventos que ocorrem nos controles usados na interface MainWindow1.xaml.

Estaremos usando a classe Acesso e usar os métodos ali definidos para acessar e persistir as informações.

O evento Loaded() irá ser executado quando o formulário for carregado e nele iremos exibir os dados na interface;

   private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                ExibeDados();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erro : " + ex.Message);
                this.Close();
            }
        }

No botão incluir temos o código que chama o método Insert da classe Acesso. Primeiro validamos as informações e a seguir incluímos o usuário e exibimos os dados:

private void btnIncluir_Click(object sender, RoutedEventArgs e)
        {
            string usuario = txtUsuario.Text;
            string senha = txtSenha.Text;
            if (ValidaDados())
            {
                try
                {
                    Acesso.Insert(usuario, senha);
                    ExibeDados();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(" Erro : " + ex.Message, "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

No evento Click do botão Deletar o solicitamos a confirmação e verificamos se existe um usuário selecionado no ListView para obtermos o seu id e usarmos o método Delete() para excluir o usuário;

Para obter o id do usuário selecionado no ListView obtemos um DataRowView que representa um modo de exibição personalizado de um DataRow e a seguir usamos a propriedade Row para obter o DataRow visualizado que no caso é a coluna zero (Row[0]) representando o id do usuário;

private void btnDeletar_Click(object sender, RoutedEventArgs e)
        {
            if (MessageBox.Show("Confirma exclusão deste registro ?", "Excluir", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
            {
                if (lstvUsuarios.SelectedItems.Count > 0)
                {
                    DataRowView drv = (DataRowView)lstvUsuarios.SelectedItem;
                    int id = Convert.ToInt32(drv.Row[0].ToString());
                    try
                    {
                        Acesso.Delete(id);
                        ExibeDados();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(" Erro : " + ex.Message, "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
            }
        }

Para atualizar um usuário temos que selecioná-lo no ListView, validar as informações, obter o seu id no ListView e então usar o método Update();

      private void btnAtualizar_Click(object sender, RoutedEventArgs e)
        {
            string usuario = txtUsuario.Text;
            string senha = txtSenha.Text;

            if (ValidaDados())
            {
                if (lstvUsuarios.SelectedItems.Count > 0)
                {
                    DataRowView drv = (DataRowView)lstvUsuarios.SelectedItem;
                    int id = Convert.ToInt32(drv.Row[0].ToString());
                    try
                    {
                        Acesso.Update(id, usuario, senha);
                        ExibeDados();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(" Erro : " + ex.Message,"Erro",MessageBoxButton.OK,MessageBoxImage.Error);
                    }
                }
            }
        }

A rotina ExibeDados() usa o método getTabela para obter um DataTable contendo os usuários cadastrados e exibí-los no ListView;

   public void ExibeDados()
        {
            DataTable dtb = Acesso.getTabela();
            lstvUsuarios.DataContext = dtb.DefaultView;
        }

No botão Limpar , atribuimos um valor vazio as caixas de texto e colocamos o foco no nome do usuário;

private void btnLimpar_Click(object sender, RoutedEventArgs e)
        {
            txtUsuario.Text = "";
            txtSenha.Text = "";
            txtUsuario.Focus();
        }

A rotina de validação é bem simples e apenas verifica se as informações não estão em branco ou possuem um tamanho menor que 6 caracteres;

      private bool ValidaDados()
        {
            bool retorno;
            if (txtSenha.Text == string.Empty || txtSenha.Text.Length < 6 )
            {
                MessageBox.Show("Informe a senha do Usuário com no mínimo seis caracteres.", "Senha", MessageBoxButton.YesNo, MessageBoxImage.Error);
                txtSenha.Focus();
                return false;
            }
            else
            {
                retorno = true;
            }
            if (txtUsuario.Text == string.Empty || txtUsuario.Text.Length < 6)
            {
                MessageBox.Show("Informe o nome do Usuário com no mínimo seis caracteres.", "Senha", MessageBoxButton.YesNo, MessageBoxImage.Error);
                txtSenha.Focus();
                return false;
            }
            else
            {
                retorno = true;
            }
            return retorno;
        }

Executando o projeto teremos o seguinte resultado:

Sugestões para melhorias no projeto:

Pegue o projeto completo aqui: ControlaUsuarios.zip

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

Referências:

José Carlos Macoratti