C# - Cadastro de Alunos com foto no SQL Server II


Na primeira parte deste artigo eu criei o projeto e defini o seu escopo, em seguida criei o banco de dados e as stored procedures usadas no projeto e por fim criei a interface da aplicação. Pois bem falta agora definir o código que vai realmente fazer tudo funcionar.

Lembrando que pretendemos realizar as seguintes tarefas:

As tarefas são simples e em diversos artigos eu já mostrei como realizar cada uma delas, mas resolvi criar este projeto simples para ajudar quem esta começando na linguagem C#,

Eu estou usando os seguintes recursos :

A interface do projeto foi definida no formulário fom1.cs e possui o seguinte leiaute:(leiaute ou lay-out ?)

Definindo o código do projeto

O que vamos fazer agora é usar os eventos dos controles dos formulários como Click, KeyPress, Change,etc. para inserir o código respectivo referente à tarefa que desejamos executar.

É uma boa prática fazer isso ?

Não, não é.

Sempre que criarmos um projeto, devemos separar as responsabilidades, e, uma das primeiras coisas que devemos fazer é definir a arquitetura da nossa aplicação.

O ideal é separar as responsabilidades em camadas de forma a que elas sejam independentes uma das outras com o objetivo de facilitar a manutenção e poder reutilizar o código.

Quando colocamos na interface, como vamos fazer neste exemplo, código que acessa os dados usando referências a objetos ADO .NET estamos misturando na interface responsabilidades que não deveriam estar presentes. A interface deveria saber somente de responsabilidades da interface como exibição e formatação de dados.

O acesso aos dados deveria estar em uma camada distinta e somente ela seria responsável por esta tarefa. Da mesma forma nela não devem existir tarefas relacionadas com a interface como uso de controles para exibir informações , etc.

Como o projeto é bem simples e estamos tratando dele para o nível iniciante não vou me preocupar em definir camadas e vou usar os eventos dos controles do formulário para colocar o código que vai fazer todo o serviço que desejamos.

A primeira tarefa é definir os namespaces no formulário.

Podemos dizer que os Namespaces organizam os objetos em um assembly ; assim , um assembly podem conter um ou mais namespaces , e estes namespaces pode conter um ou mais namespaces. Desta forma os Namespaces evitam a ambiguitdade e organizam referências quando são usados grande grupos de objetos como as librarys de classes.

Um assembly é unidade primária de construção da plataforma .NET , ou seja , é o tijolo da plataforma sobre o qual tudo o mais se apoia. Um assembly pode ser reutilizado e é auto-descritivo de maneira a permitir que um runtime .NET possa gerenciar plenamente a aplicação.

Comece sempre por este item , pois é ele que vai definir quais classes você vai poder acessar e se durante a definição do código precisarmos acessar outros recursos basta incluir o novo namespace.

A seguir temos os namespaces que iremos usar:

using System;
using System.Data;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing;
using Microsoft.VisualBasic;

Após isso vamos definir as variáveis que serão visíveis em todo o formulário. Lembre-se que se definirmos uma variável no interior de um método dependendo do seu escopo ela não será visível em outro método ou evento no projeto.

A seguir temos as variáveis que iremos usar no projeto:

SqlConnection con;
SqlCommand cmd;
SqlDataAdapter adapter;
DataSet ds;
int linhaNumero = 0;
MemoryStream ms;
byte[] foto_array;

As 4 primeiras variáveis vão tratar de acesso e tratamento dos dados e as demais da navegação e da conversão da foto em dados binários.

O primeiro evento que iremos usar é o evento que é executado quando abrirmos o formulário form1.cs, o evento Load;

   private void Form1_Load(object sender, EventArgs e)
   {
            string strcon = @"Data Source=.\SQLEXPRESS;AttachDbFilename=c:\dados\Escola.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
            con = new SqlConnection(strcon);
            carregaDados();
            exibe_Dados();
  }

Como ele é um dos primeiros eventos que antes do formulário ser aberto vamos definir neste evento o seguinte:

A string de conexão usa o arquivo SQL Server Escola.mdf que esta na pasta c:\dados.Não é uma boa prática definir a string de conexão no código pois dificulta a manutenção e pode levar a erros.

O ideal seria colocá-la no arquivo de configuração App.Config e obtê-la usando a classe ConfigurationManager, mas para não complicar as coisas eu defini a string de conexão na variável strcon;

Usamos em seguida esta string de conexão para abrir uma conexão com o banco de dados.

O código da rotina carregaDados() é dado abaixo:

        void carregaDados()
        {
            cmd = new SqlCommand("getAlunos", con);
            cmd.CommandType = CommandType.StoredProcedure; 
            adapter = new SqlDataAdapter(cmd);
            adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
            ds = new DataSet(); 
            adapter.Fill(ds, "Alunos");
        }

Nesta rotina estamos usando a stored procedure getAlunos que seleciona os alunos da tabela Alunos e preenchendo um DataSet chamado Alunos deixando-o pronto para ser usado.

A rotina exibeDados possui o seguinte código:

void exibe_Dados()
{
if (ds.Tables[0].Rows.Count > 0)
{
   
txtCodigo.Text = ds.Tables[0].Rows[linhaNumero][0].ToString();
   txtNome.Text = ds.Tables[0].Rows[linhaNumero][1].ToString();
   txtCurso.Text = ds.Tables[0].Rows[linhaNumero][2].ToString();
   txtMensalidade.Text = String.Format("{0:C}",ds.Tables[0].Rows[linhaNumero][3].ToString());


  picFoto.Image = null;

  if (ds.Tables[0].Rows[linhaNumero][4] != System.DBNull.Value)
  {
     
foto_array = (byte[])ds.Tables[0].Rows[linhaNumero][4];
     MemoryStream ms = new MemoryStream(foto_array);
     picFoto.Image = Image.FromStream(ms);

  }
}
else
{
  MessageBox.Show("Não há registros na tabela");
}
}

O código acima verifica se o dataset possui linhas e exibe os dados do registro correspondente nos controles de formulário.

Verificamos também se a imagem do aluno atribuída é nula e caso isso não ocorra definimos um array de bytes com os dados binários da imagem e convertemos esses dados em uma imagem para ser exibida no controle PicutreBox.

Para escolher uma imagem a ser atribuída como foto ao aluno temos o botão Escolher e no seu evento Click incluímos o seguinte código:

  private void ProcurarFoto_Click(object sender, EventArgs e)
  {
         oFd1.Filter = "jpeg|*.jpg|bmp|*.bmp|all files|*.*";
         DialogResult res = oFd1.ShowDialog();
  
          if (res == DialogResult.OK)
            {
                picFoto.Image = Image.FromFile(oFd1.FileName);
            }
 }

Ao clicar no botão escolar o controle OpenFileDialog(Ofd1) irá abrir uma caixa de diálogo para que uma imagem seja escolhida. Ao fazer isso a imagem do arquivo selecionado é exibido no controle PictureBox - picFoto.

Para procurar um aluno já cadastro temos o botão Procurar cujo código do evento Click é o seguinte:

  private void Procurar_Click(object sender, EventArgs e)
    {
            try
            {
                int n = Convert.ToInt32(Interaction.InputBox("Informe o código do Aluno:", "Procurar", "1", 450, 400));
                DataRow drow;
                drow = ds.Tables[0].Rows.Find(n);
                if (drow != null)
                {
                    linhaNumero = ds.Tables[0].Rows.IndexOf(drow);
                    txtCodigo.Text = drow[0].ToString();
                    txtNome.Text = drow[1].ToString();
                    txtCurso.Text = drow[2].ToString();
                    txtMensalidade.Text = drow[3].ToString();
                    picFoto.Image = null;
                    if (drow[4] != System.DBNull.Value)
                    {
                        foto_array = (byte[])drow[4];
                        MemoryStream ms = new MemoryStream(foto_array);
                        picFoto.Image = Image.FromStream(ms);
                    }
                }
                else
                    MessageBox.Show("Registro não localizado.");
            }
            catch
            {
                MessageBox.Show("Dados inválidos.");
            }
        }

Neste código usamos o método InputBox para solicitar que seja informado o código do aluno.

A seguir usamos o método Find para localizar o registro na tabela e exibimos as informações no formulário.

Agora eu vou definir o código que permite navegar pelos registros e que será inserido no evento Click dos botões de comando

Abaixo temos o código dos 4 botões de comando para realizar a navegação:

        private void Primeiro_Click(object sender, EventArgs e)
        {
            linhaNumero = 0; 
            exibe_Dados();
            MessageBox.Show("Primeiro registro");
        }

        private void Anterior_Click(object sender, EventArgs e)
        {
            if (linhaNumero > 0)
            {
                linhaNumero--; 
                exibe_Dados();
            }
            else
                MessageBox.Show("Primeiro registro");
        }

        private void Proximo_Click(object sender, EventArgs e)
        {
            if (linhaNumero < ds.Tables[0].Rows.Count - 1)
            {
                linhaNumero++; 
                exibe_Dados();
            }
            else
                MessageBox.Show("Úlitmo registro");
        }

        private void Ultimo_Click(object sender, EventArgs e)
        {
            linhaNumero = ds.Tables[0].Rows.Count - 1;
            exibe_Dados(); 
            MessageBox.Show("Úlitmo registro");
       }

A navegação é baseada na variável linhaNumero que representa o índice do registro atual da tabela e na rotina exibe_Dados() que exibe os registros do índice atual representando por linhaNumero.

Vejamos agora o código de cada um dos botões que realizam as tarefas de incluir, atualizar e deletar registros da tabela:

1- Botão Novo

O botão Novo apenas limpa os controles do formulário e põe o foco no controle txtNome que é caixa de texto para inserir o nome do aluno, dessa forma basta o usuário digitar as informações e clicar no botão Incluir;

private void btnNovo_Click(object sender, EventArgs e)
 {
         txtCodigo.Text = txtNome.Text = txtCurso.Text = txtMensalidade.Text = "";
         picFoto.Image = null;
         txtNome.Focus();
}

2- Botão Incluir

No evento Click do botão Incluir temos o código que usa stored procedure inserir_alunos , passa os parâmetros referente aos dados que serão incluídos , converte a foto para o formato binário, inclui e exibe os registros no formulário;

        private void Inserir_Click(object sender, EventArgs e)
        {
            cmd = new SqlCommand("inserir_Alunos", con);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@nome", txtNome.Text);
            cmd.Parameters.AddWithValue("@curso", txtCurso.Text);
            cmd.Parameters.AddWithValue("@mensalidade", txtMensalidade.Text);
            converterFoto();
            con.Open();
            int n = cmd.ExecuteNonQuery();
            con.Close();
            if (n > 0)
            {
                MessageBox.Show("registro inserido com sucesso.");
                carregaDados();
            }
            else
                MessageBox.Show("Erro ao inserir registro.");
        }

3- Botão Atualizar

No evento Click do botão Atualizar repetimos os mesmos passos usando a stored procedure atualiza_alunos;

   private void Atualizar_Click(object sender, EventArgs e)
    {
            cmd = new SqlCommand("atualiza_Alunos", con);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@id", txtCodigo.Text);
            cmd.Parameters.AddWithValue("@nome", txtNome.Text);
            cmd.Parameters.AddWithValue("@curso", txtCurso.Text);
            cmd.Parameters.AddWithValue("@mensalidade", txtMensalidade.Text);
            converterFoto();
            con.Open();
            int n = cmd.ExecuteNonQuery();
            con.Close();
            if (n > 0)
            {
                MessageBox.Show("Registro atualizado");
                carregaDados();
            }
            else
                MessageBox.Show("Erro ao atualizar dados.");
   }

4- Botão Deletar

No evento Click do botão Deletar usamos a propriedade DialogResult que representa o valor que é retornado do formulário obtido na janela de diálogo apresentada. No caso estamos solicitando a confirmação para excluir o registro.

A seguir usamos a stored procedure exclui_alunos e passamos como parâmetro o código do aluno informado na caixa de texto txtCodigo;

  private void Excluir_Click(object sender, EventArgs e)
  {
       DialogResult resultado = MessageBox.Show("Confirma exclusão deste Aluno ?", "Confirma Exclusão", 
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
    
    if(resultado == DialogResult.Yes)
            {
                cmd = new SqlCommand("exclui_Alunos", con);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@id", Convert.ToInt32(txtCodigo.Text));
                con.Open();
                int n = cmd.ExecuteNonQuery();
                con.Close();

                if (n > 0)
                {
                    MessageBox.Show("Registro Excluído");
                    carregaDados();
                    linhaNumero = 0;
                    exibe_Dados();
                }else{
                    MessageBox.Show("Erro ao excluir registro.");
                }
      }
  }

Após excluir um registro carregamos os dados novamente.

Estamos usando o evento KeyPress do controle TextBox - txtMensalidade, para permitir que somente valores numéricos sejam informados neste controle:

    private void txtMensalidade_KeyPress(object sender, KeyPressEventArgs e)
   {
            if (!char.IsNumber(e.KeyChar) && !(e.KeyChar == '.') && !(e.KeyChar == Convert.ToChar(8)))
            {
                e.Handled = true;
            }
  }

A rotina ConverteFoto é a responsável por converter a imagem para dados binários antes de gravar no banco de dados:

      void converterFoto()
        {
            //convertendo a foto para dados binários
            if (picFoto.Image != null)
            {
                ms = new MemoryStream();
                picFoto.Image.Save(ms, ImageFormat.Jpeg);
                byte[] foto_array = new byte[ms.Length];
                ms.Position = 0;
                ms.Read(foto_array, 0, foto_array.Length);
                cmd.Parameters.AddWithValue("@foto", foto_array);
            }
        }

E assim criamos um projeto usando os recursos da ADO .NET via código e do SQL Server usando a linguagem C#, onde temos todo o código necessário para tratar as informações dos alunos e realizar as operações CRUD (Create,Read, Update e Delete) na tabela Alunos.

Executando o projeto e gravando as informações para um aluno com foto obtemos:

Podemos melhorar o projeto criando a tal camada de acesso a dados e separando o código de acesso a dados da interface. Podemos também implementar um tratamento de erros mais eficaz, e incluir outros recursos. Fique a vontade...

Simples, simples assim...

Para encerrar rei implementar a rotina para imprimir os dados do aluno com foto. Aguarde...

O projeto completo esta no Super DVD .NET

Eu sei é apenas C# mas eu gosto...

Referências:


José Carlos Macoratti