WebMobile 15
Revista WebMobile Edição 15.
WebMobile Edição 15

Utilizando Orientação a Objetos no desenvolvimento de aplicações Web

O desenvolvimento de sistemas de computador durante muito tempo foi tarefa árdua de se realizar. Os códigos, para resolverem o mesmo problema, eram escritos com padrões de codificação diferentes entre os sistemas e quando se fazia necessário utilizá-los novamente eram copiados de um lugar para outro, isso quando tinha-se a noção de onde encontrá-los.

Para conseguir que os esforços, realizados pelos programadores, sejam utilizados em outros sistemas ou por outras pessoas, foi criada a programação orientada a objetos (POO). Esta por sua vez, é baseada na interação entre as unidades do software que são chamadas objetos.

Na programação Orientada a Objetos são implementadas as classes que regem as regras de comportamento (esses são os métodos), seus possíveis estados (esses são os atributos) e o relacionamento entre as classes.

O intuito deste artigo é desenvolvermos um trecho de aplicação fazendo o uso de algumas práticas de desenvolvimento em POO.

Cenário

Nossas atividades neste artigo consistirão em desenvolvermos um sistema de controle de delegação das atividades de um projeto, para a equipe de desenvolvimento envolvida no projeto. Portanto temos os seguintes requisitos:

Cadastro de Função
Permitir a manutenção (visualização, inclusão, exclusão e alteração) das funções que um colaborador poderá exercer.
Cadastro de Colaboradores
  • Permitir a manutenção (visualização, inclusão, exclusão e alteração) dos colaboradores;
  • Caso um colaborador já possua tarefas, o mesmo não poderá ser excluído, portanto deverá ser inativo;
  • Quando um colaborador for inativo ou excluído, as atividades do projeto deverão ser desatribuídas no mesmo.
Cadastro de Clientes
  • Permitir a manutenção dos clientes;
  • Caso um cliente já possua projetos, o mesmo não poderá ser inativo enquanto os projetos não estiverem finalizados;
  • Caso o cliente possua projetos, porém finalizados, o mesmo deve ser inativo.
Cadastro de Projetos
Permitir a manutenção dos projetos.
Cadastro de Atividades do Projeto
Permitir a manutenção das atividades do projeto.
Cadastro de Tarefas
  • Permitir a manutenção das tarefas do colaborador;
  • A tarefa poderá ser referenciada a uma atividade de um determinado projeto.

O modelo da aplicação

A aplicação será desenvolvida em três camadas, aonde: camada de dados (Data Access Layer - DAL) é o banco de dados e as classes, que manipulam as informações do mesmo, camada de negócio (Business Layer - BSN) são as classes que contém todas as funções e regras da aplicação e camada de apresentação (User Interface Layer - UI) serão as páginas ASPX que interagem diretamente com o usuário.

Nota: BSN também é comumente chamado de BLL (Business Logic Layer).

Teremos, também, uma classe que representará um registro, chamada de Classe de Estado Cheia (State Full Class - STF) que será transferida entre as camadas. A Figura 1 mostra o modelo da aplicação e a dependência entre as camadas.

Modelo da aplicação e dependência entre as camadas
Figura 1. Modelo da aplicação e dependência entre as camadas.

Nota: usar uma STF tem inúmeras vantagens. Ela funciona como um “protocolo” de comunicação entre as diferentes camadas da solução, que trocam dados somente através dessa entidade. Isso garante melhor independência e reutilização das camadas, além de ser uma boa prática de desenvolvimento. Tudo que a camada DAL faz, por exemplo, é receber objetos STF para transformá-los em comandos SQL e persisti-los no banco de dados. Usar uma STF torna a solução totalmente abstraída do tipo de interface que irá exibi-la, o tipo de camada de acessos a dados que irá persisti-la e como ela será validada pela camada de negócio. Tudo o que ela faz é guardar os respectivos dados das entidades envolvidas.

As classes de apoio, ou seja, conexão com banco de dados, criptografia, funções gerais etc. não foram especificadas em nenhuma das camadas porque essas classes podem ser utilizadas a qualquer momento e por qualquer camada.

O banco de dados

Utilizaremos o Mysql 5.0 para armazenar os dados da aplicação. Vale a pena reforçar que não é intuito deste artigo explicar os passos de instalação e criação do banco de dados.

O banco de dados é composto pelas seguintes tabelas:

  • FUNC: dados referentes às funções que um colaborador poderá exercer;
  • COLAB: dados referentes a colaboradores;
  • CLIENT: dados referentes a clientes;
  • PROJT: dados referentes aos projetos;
  • ATPRJ: dados referentes às atividades do projeto;

A Figura 2 mostra o modelo do banco de dados contendo as tabelas com seus atributos e relacionamentos.

Modelo de dados da aplicação
Figura 2. Modelo de dados da aplicação.

Criando a aplicação

Começaremos com a preparação do ambiente da aplicação. Abra o Visual Studio 2005 e clique em File>New>Project, escolha a linguagem Visual C# e aponte para o item Class Library.

Neste ponto, informe em Name Colab.Code e selecione o local da aplicação, em nosso exemplo: C:\Projetos\ColabProject\. A Figura 3 apresenta as propriedades da tela de criação de projetos.

Propriedades da tela de criação de projetos
Figura 3. Propriedades da tela de criação de projetos.

Esse projeto será responsável por armazenar os arquivos referentes às classes DAL, BSN e STF da nossa aplicação. Estamos fazendo isso, para encapsular em apenas uma DLL todo o código necessário para a regra de negócio e persistência da aplicação.

Precisamos criar também, o projeto referente à camada de apresentação da aplicação. A aplicação fará uso do ASP.NET AJAX (ajax.asp.net) para torná-la mais dinâmica e evitar o carregamento das páginas como um todo.

No Solution Explorer, clique com o botão direito e escolha Add>New Web Site. Selecione o item ASP.NET AJAX-Enabled WebSite e dê o nome de Colab.UI e selecione o local da aplicação, em nosso exemplo: C:\Projetos\ColabProject\Colab.UI. A Figura 4 apresenta as propriedades da tela de criação de projetos.

Propriedades da tela de criação de projetos
Figura 4. Propriedades da tela de criação de projetos.

Agora, no projeto, precisamos referenciar o Colab.Code. Para isso, no Solution Explorer clique com o botão direito no Colab.UI e escolha Add Reference. Na janela que é apresentada clique na aba Projects, selecione Colab.Code e clique no OK. A Figura 5 mostra a janela para adicionar referências em um projeto.

 Tela para adicionar referências aos projeto
Figura 5. Tela para adicionar referências aos projeto.

Classe de conexão com banco de dados

Primeiramente, vamos realizar as configurações necessárias para conectarmos no banco de dados, essas configurações serão armazenadas no Web.config na subsessão appSettings da sessão configuration, abaixo do elemento configSections. A Listagem 1 contém as chaves que serão incluídas no Web.config.

Listagem 1. Chaves de configuração no Web.Config.

<appSettings>

        

         <add key="database_server" value="localhost"/>

         <add key="database_name" value="colab_projt"/>

         <add key="database_user" value="colab_projt"/>

         <add key="database_pwd" value="colab_projt"/>

</appSettings>

Agora que temos os dois projetos e o Web.config foi alterado para conter as informações necessárias para realizar a conexão com o banco de dados, criaremos a classe de conexão com banco de dados.

Porém, não devemos esquecer que ainda temos que fazer a referência ao provider de acesso ao MySql 5.0 (dev.mysql.com/downloads/connector/net/5.0.html). Você pode usar o banco de dados que desejar, desde que o mesmo tenha um provider para o .NET.

Para fazer a referência do provider no Solution Explorer, clique com o botão direito sobre o nome do projeto e escolha o item Add Reference. Na janela que é apresentada, clique na aba Browse e localize o arquivo do DLL do provider, a Figura 6 mostra a janela para adicionar a referência.

Seleção do provider do MySql 5.0
Figura 6. Seleção do provider do MySql 5.0.

Agora que a referência com o provider está feita, vamos ao código da classe de conexão. No Solution Explorer, clique com o botão direito sobre o Colab.Code e escolha Add>Class. Na janela que é apresentada selecione o item Class e no campo Name digite “DataBaseConn.cs”.

A classe de conexão terá métodos para: abrir (OpenConnection) e fechar (CloseConnection) a conexão, realizar a execução de comandos SQL (ExecuteDataSet, ExecuteReader, ExecuteSQL) e propriedades (HostName, DatabaseName, UserName, UserPassword, ReturnConnection) para realizar o acesso às variáveis internas à classe.

Começaremos a implementação dessa classe pelas propriedades e o construtor, esse último é responsável por retornar as configurações do banco de dados que realizamos anteriormente no Web.config. Adicione no projeto a referência do assembly System.Configuration. A Listagem 2 mostra o código referente às propriedades e ao construtor da classe.

Listagem 2. Propriedades e construtor da classe de conexão.

using System;

using System.Collections;

using System.Configuration;

using System.Data;

using System.Data.Common;

using System.Text;

using System.Web;

using MySql.Data.MySqlClient;

namespace ColabProject.Util

{

  public class DataBaseConn

  {

    private MySqlTransaction mObjTransaction;

    private MySqlConnection mObjConnection;

    private string mStrServer = string.Empty;

    private string mStrDatabase = string.Empty;

    private string mStrUserName;

    private string mStrPassword;

    //Propriedades

    public string UserName

    {

      get {
        return mStrUserName;
      }

      set {
        mStrUserName = value;
      }

    }

    public string UserPassword

    {

      get {
        return mStrPassword;
      }

      set {
        mStrPassword = value;
      }

    }

    public string HostName

    {

      get {
        return mStrServer;
      }

      set {
        mStrServer = value;
      }

    }

    public string DatabaseName

    {

      get {
        return mStrDatabase;
      }

      set {
        mStrDatabase = value;
      }

    }

    public MySqlConnection ReturnConnection

    {

      get {
        return mObjConnection;
      }

    }

    //Métodos públicos

    public DataBaseConn()

    {

      mObjConnection = new MySqlConnection();

      this.HostName =

        ConfigurationManager.AppSettings.Get(

          "database_server");

      this.DatabaseName =

        ConfigurationManager.AppSettings.Get(

          "database_name");

      this.UserName =

        ConfigurationManager.AppSettings.Get(

          "database_user");

      this.UserPassword =

        ConfigurationManager.AppSettings.Get(

          "database_pwd");

    }

  }

Implementamos portanto, as propriedades e o método construtor que retorna as configurações do Web.config, podemos implementar o OpenConnection e CloseConnection (Listagem 3) que são responsáveis respectivamente por abrir e fechar a conexão com o banco de dados.

No OpenConnection, a conexão com o banco de dados só será efetuada caso ainda não tenha sido feita. Para garantirmos esse funcionamento, verificaremos se o estado da conexão é igual a interrompida (Broken) ou fechada (Closed).

Listagem 3. Métodos para abrir e fechar a conexão com o banco.

public void OpenConnection()

{

  string lStrConnectionString = "Data Source=" +

    this.HostName + ";user id=" + this.UserName +

    "; password=" + this.UserPassword +

    "; database=" + this.DatabaseName + ";";

  bool lBlnOpen = false;

  if (mObjConnection == null)

  {

    lBlnOpen = true;

  }

  if (!lBlnOpen)

  {

    //Verifica se a conexão está fechada ou

    //interrompida

    if ((mObjConnection.State ==

        ConnectionState.Broken) || (

        mObjConnection.State == ConnectionState.Closed))

    {

      lBlnOpen = true;

    }

  }

  if (lBlnOpen)

  {

    mObjConnection = new MySqlConnection();

    mObjConnection.ConnectionString =

      lStrConnectionString;

    mObjConnection.Open();

  }

}

public void CloseConnection()

{

  mObjConnection.Close();

  mObjConnection.Dispose();

  mObjConnection = null;

}

O ExecuteSQL é responsável por realizar a execução de comandos Insert, Update e Delete. Para garantir que somente esses métodos serão executados, verificamos se o SQL passado no parâmetro inicia-se por SELECT.

Os métodos ExecuteDataSet e ExecuteDataReader serão utilizados para executarmos comandos select, que retornarão um ou mais resultados. A Listagem 4 mostra o código do métodos.

Listagem 4. Métodos para execução de comandos SQL.

public long ExecuteSQL(string pSQL)

{

  int lIntRows;

  try

  {

    if (pSQL.Substring(0, 6).ToUpper().Equals(

        "SELECT"))

    {

      Exception e = new Exception(

        "ExecuteSQL - O SQL não pode ser de seleção " +

        "de registros.");

      throw e;

    }

    this.OpenConnection();

    MySqlCommand lObjCommand = new MySqlCommand();

    lObjCommand.CommandType =

      System.Data.CommandType.Text;

    lObjCommand.CommandText = pSQL;

    lObjCommand.Connection = mObjConnection;

    lIntRows = lObjCommand.ExecuteNonQuery();

    lObjCommand.Dispose();

  } catch (Exception eExecute)

  {

    throw eExecute;

  }

  return lIntRows;

}

public DataSet ExecuteDataSet(string pSQL)

{

  DataSet lDtsDataSet = new DataSet();

  try

  {

    //Abrir a conexão com o banco de dados

    this.OpenConnection();

    MySqlCommand lObjCommand = new MySqlCommand();

    lObjCommand.CommandType =

      System.Data.CommandType.Text;

    lObjCommand.CommandText = pSQL;

    lObjCommand.Connection = mObjConnection;

    MySqlDataAdapter lObjDataAdapter =

      new MySqlDataAdapter();

    lObjDataAdapter.SelectCommand = lObjCommand;

    lObjDataAdapter.Fill(lDtsDataSet);

    lObjDataAdapter.Dispose();

    lObjCommand.Dispose();

    return lDtsDataSet;

  } catch (Exception eExecute)

  {

    throw eExecute;

  }

}

public IDataReader ExecuteReader(string pSQL)

{

  IDataReader lObjDataReader;

  try

  {

    //Abrir a conexão com o banco de dados

    this.OpenConnection();

    MySqlCommand lObjCommand = new MySqlCommand();

    lObjCommand.CommandType =

      System.Data.CommandType.Text;

    lObjCommand.CommandText = pSQL;

    lObjCommand.Connection = mObjConnection;

    lObjDataReader = lObjCommand.ExecuteReader();

    lObjCommand.Dispose();

    return lObjDataReader;

  } catch (NullReferenceException nExecute)

  {

    throw nExecute;

  } catch (Exception eExecute)

  {

    throw eExecute;

  }

}

Construindo o menu principal e a página básica

A interface da aplicação será composta por três partes básicas: topo, direita e esquerda. No topo, teremos o título da aplicação, à direita o menu principal e à esquerda as telas do sistema.

Primeiramente, construiremos um UserControl que conterá o código necessário para o menu da aplicação. No Solution Explorer clique com o botão direito sobre a aplicação Web e escolha Add New Item. Na tela que é apresentada (Figura 7), selecione o item Web User Control, no campo Name digite wucMainMenu.ascx.

Criando o UserControl para o menu principal
Figura 7. Criando o UserControl para o menu principal.

O menu da aplicação será formado através do TreeView. A Listagem 5 mostra o código necessário para montar o TreeView de acordo com os itens que precisamos no menu.

Listagem 5. Código HTML do wucMainMenu.ascx.

<asp:TreeView ID="trvMainMenu" runat="server"

  NodeIndent="15" OnSelectedNodeChanged="trvMainMenu_SelectedNodeChanged" ShowExpandCollapse="False" ShowLines="True"

ForeColor="Black">

<Nodes>

  <asp:TreeNode Text="Menu Principal"

    Value="MNU_PRINCIPAL">

  <asp:TreeNode Text="Cadastros Básicos"

    Value="MNU_CAD_BASICOS">

  <asp:TreeNode Text="Tipo de Funções"

    Value="MNU_CAD_BASICOS_TPFUNC"></asp:TreeNode>

  </asp:TreeNode>

  <asp:TreeNode Text="Cadastros de Pessoas"

    Value="MNU_CAD_PSSOA">

  <asp:TreeNode Text="Cadastros de Clientes"

    Value="MNU_CAD_PSSOA_CLIENT">asp:TreeNode>

  <asp:TreeNode Text="Cadastros de Colaboradores"

    Value="MNU_CAD_PSSOA_USURIO"></asp:TreeNode>

  </asp:TreeNode>

  <asp:TreeNode Text="Projetos" Value="PROJETOS">

  </asp:TreeNode>

  <asp:TreeNode Text="Tarefas" Value="TAREFAS">

  </asp:TreeNode>

  </asp:TreeNode>

</Nodes>

</asp:TreeView>

Para que o menu funcione, será necessário implementarmos o evento SelectedNodeChanged, onde direcionaremos para a página referente à opção selecionada, a Listagem 6 mostra o código para o evento.

Listagem 6. Código do evento SelectedNodeChanged do TreeView.

protected void trvMainMenu_SelectedNodeChanged(

  object sender, EventArgs e)

{

  switch (trvMainMenu.SelectedValue)

  {

  case "MNU_CAD_BASICOS_TPFUNC":

    Response.Redirect("Display.aspx?trv=FUNC");

    break;

  case "MNU_CAD_BASICOS_TPENV":

    Response.Redirect("Display.aspx?trv=TPENV");

    break;

  case "MNU_CAD_PSSOA_CLIENT":

    Response.Redirect("Display.aspx?trv=CLIENT");

    break;

  case "MNU_CAD_PSSOA_USURIO":

    Response.Redirect("Display.aspx?trv=COLAB");

    break;

  case "PROJETOS":

    Response.Redirect("Display.aspx?trv=PROJT");

    break;

  case "TAREFAS":

    Response.Redirect("Display.aspx?trv=TARFS");

    break;

  default:

    Response.Redirect("Default.aspx");

    break;

  } //switch

}

Como você pode ter notado, escrevemos todo o código necessário para montar o TreeView do menu principal e também fazer o direcionamento para a página correta, ao clicar sobre um item. Existe uma outra maneira de realizar esse trabalho que é fazendo a utilização do SiteMap.

No Solution Explorer clique com o botão direito sobre a aplicação Web e escolha o item Add New Item. Na tela que é apresentada (Figura 8) selecione o item Master Page, no campo Name digite “mpgBasica.master” e selecione a opção Place code in separete file.

Adicionando a Master Page ao projeto
Figura 8. Adicionando a Master Page ao projeto.
Listagem 7. Código da mpgBasica.master.

<html xmlns="http://www.w3.org/1999/xhtml" >
   <head runat="server">
      <title>
      title>
      head>
   <body style="margin-top:0;margin-right:0;
      margin-bottom:0;margin-left:0">
      <form id="_frm" runat="server">
         <asp:ScriptManager ID="scmGeral" runat="server">
         </asp:ScriptManager>
         <table cellpadding="0" cellspacing="0" border="0"
            height="100%" width="100%">
            <tr>
               <td colspan="2" align="center" valign="middle"
                  height="80px" nowrap="nowrap" bgcolor="SteelBlue">
                  <asp:Label ID="lblTitulo" runat="server"
                     Text="Colaboração em Projetos"
                     Font-Names="Arial Black" Font-Size="XX-Large">
                  </asp:Label>
               </td>
            </tr>
            <tr>
               <td valign="top" style="width:220px;
                  padding-bottom:10px;padding-left:10px;
                  padding-top:10px;padding-right:10px"
                  nowrap="nowrap">
                  <uc1:wucMainMenu ID="wucMainMenu"
                     runat="server" />
               </td>
               <td valign="top" style="width:100%;
                  padding-bottom:10px;padding-left:20px;
                  padding-top:10px;padding-right:20px" >
                  <asp:contentplaceholder id="cphCentro"
                     runat="server">
                  </asp:contentplaceholder>
               </td>
            </tr>
         </table>
      </form>
   </body>
</html>
Layout da Master Page
Figura 9. Layout da Master Page.

Construindo a ToolBar para as telas de cadastro

Nas páginas vinculadas à Master Page teremos uma ToolBar (barra de ferramentas) com botões padrões para as ações: novo, salvar, excluir e voltar, conforme a ilustrado na Figura 10. Também será função da ToolBar exibir, acima dos botões, o título da página em questão.

ToolBar das ações padrões
Figura 10. ToolBar das ações padrões.

Para construirmos a ToolBar, adicionaremos um novo User Control, no projeto, que chamaremos de wucToolBar. No HTML do controle, disponível na Listagem 8, deve ser incluído um Label (lblTitulo) e quatro ImageButtons (btnNovo, btnSalvar, btnExcluir, btnVoltar) nos quais escreveremos o evento Click.

Listagem 8. Código do wucToolBar.ascx.

<table cellSpacing="0" cellPadding="0" width="100%"
   border="0">
   <tr>
      <td valign="middle">
         <asp:Label id="lblTitulo" runat="server"
            Font-Bold="True"
            Font-Names="Verdana,Arial,Helvetica,sans-serif"
            Font-Size="18px" ForeColor="#C00000">
         </asp:Label>
      </td>
   </tr>
   <tr>
      <td align="left" valign="middle" height="21"
         bgColor="#5D7B9D" style="padding-top:3px;
         padding-bottom:3px">
         <table cellSpacing="2" cellPadding="0" border="0">
            <tr>
               <td style="height: 21px">
                  <asp:ImageButton id="btnNovo" runat="server"
                     ImageUrl="imgs/cmdnovo.gif"
                     ToolTip="Novo Registro"
                     OnClick="btnNovo_Click">
                  </asp:ImageButton>
                  td>
               <td style="height: 21px">
                  <asp:ImageButton id="btnSalvar" runat="server"
                     ImageUrl="imgs/cmdsalvar.gif"
                     ToolTip="Salvar Registro"
                     OnClick="btnSalvar_Click">
                  </asp:ImageButton>
               </td>
               <td style="height: 21px">
                  <asp:ImageButton id="btnExcluir" runat="server"
                     ImageUrl="imgs/cmdexcluir.gif"
                     ToolTip="Excluir Registro"
                     OnClick="btnExcluir_Click">
                  </asp:ImageButton>
               </td>
               <td style="height: 21px">
                  <asp:ImageButton id="btnVoltar" runat="server"
                     ImageUrl="imgs/cmdvoltar.gif" ToolTip="Voltar"
                     OnClick="btnVoltar_Click">
                  </asp:ImageButton>
               </td>
            </tr>
         </table>
      </td>
   </tr>
</table>

No código precisamos criar os eventos que serão disparados na página que contém um wucToolBar. Para isso, declaramos os eventos do controle e depois escrevemos o evento Click dos ImageButtons para chamar o evento que foi acionado pelo usuário. A Listagem 9 contém todo o código do controle.

Listagem 9. Criação dos eventos do controle.

//Delegates para capturar e notificar as mudanças

public delegate void NewClick(object sender,

  System.EventArgs e);

public delegate void SaveClick(object sender,

  System.EventArgs e);

public delegate void DeleteClick(object sender,

  System.EventArgs e);

public delegate void BackClick(object sender,

  System.EventArgs e);

public partial class wucToolBar:

  System.Web.UI.UserControl

{

  public event NewClick NewClick;

  public event SaveClick SaveClick;

  public event DeleteClick DeleteClick;

  public event BackClick BackClick;

  protected virtual void OnNewClick(

    System.EventArgs e)

  {

    if (NewClick != null)

    {

      NewClick(this, e);

    }

  }

  protected virtual void OnSaveClick(

    System.EventArgs e)

  {

    if (SaveClick != null)

    {

      SaveClick(this, e);

    }

  }

  protected virtual void OnDeleteClick(

    System.EventArgs e)

  {

    if (DeleteClick != null)

    {

      DeleteClick(this, e);

    }

  }

  protected virtual void OnBackClick(

    System.EventArgs e)

  {

    if (BackClick != null)

    {

      BackClick(this, e);

    }

  }

  public string PageHeader

  {

    set {
      lblTitulo.Text = value;
    }

    get {
      return lblTitulo.Text;
    }

  }

  public bool NewIsVisible

  {

    set {
      btnNovo.Visible = value;
    }

    get {
      return btnNovo.Visible;
    }

  }

  public bool SaveIsVisible

  {

    set {
      btnSalvar.Visible = value;
    }

    get {
      return btnSalvar.Visible;
    }

  }

  public bool DeleteIsVisible

  {

    set {
      btnExcluir.Visible = value;
    }

    get {
      return btnExcluir.Visible;
    }

  }

  public bool BackIsVisible

  {

    set {
      btnVoltar.Visible = value;
    }

    get {
      return btnVoltar.Visible;
    }

  }

  protected void Page_Load(object sender, EventArgs e)

  {

  }

  //Eventos dos botões sobreescritos

  protected void btnNovo_Click(object sender,

    ImageClickEventArgs e)

  {

    this.OnNewClick(System.EventArgs.Empty);

  }

  protected void btnSalvar_Click(object sender,

    ImageClickEventArgs e)

  {

    this.OnSaveClick(System.EventArgs.Empty);

  }

  protected void btnExcluir_Click(object sender,

    ImageClickEventArgs e)

  {

    this.OnDeleteClick(System.EventArgs.Empty);

  }

  protected void btnVoltar_Click(object sender,

    ImageClickEventArgs e)

  {

    this.OnBackClick(System.EventArgs.Empty);

  }

} //Classe

Construindo as classes STF, DAL, BSN

Explicarei passo a passo a criação dessas três classes e o mesmo processo deverá ser realizado para as demais tabelas do banco de dados. Utilizaremos em nosso exemplo a tabela FUNC, que se refere às funções que um colaborador poderá exercer.

No Solution Explorer clique com o botão direito sobre o projeto Colab.Code, escolha o item Add>New Folder e informe o nome da pasta como “Persistence”. Agora, dentro da pasta, incluiremos as classes. O .NET permite que um arquivo de classe contenha mais de uma classe pública, portanto reduziremos a quantidade de arquivos utilizando esse recurso.

No Solution Explorer clique com o botão direito sobre a pasta e escolha o item Add>Class e informe o nome de clsFUNC.cs. O arquivo será criado com o código padrão para uma classe, então modifique para que o código fique semelhante ao da Listagem 10. Utilizaremos Generics nas classes BSN para retornar à UI os registros retornados do banco de dados.

Listagem 10. Criação dos eventos do controle.

...

using System.Data;

using ColabProject.Util;

namespace ColabProject.Persistence.clsFUNC

{

}

STF

Conforme mencionado anteriormente, teremos no projeto classes que representam um registro do banco de dados. O objetivo principal das classes STF é ser um protocolo de comunicação entre as camadas do projeto, ou seja, entre as demais classes. Portanto serão transportadas, obedecendo a ordem lógica conforme ilustrado na Figura 11.

Ilustração do transporte das classes STF entre as demais
Figura 11. Ilustração do transporte das classes STF entre as demais.

As classes STF serão compostas por variáveis privadas, propriedades para modificação e acesso a essas variáveis e métodos para retornar a quantidade de campos e o valor de um determinado campo.

Os nomes para as classes STF obedecerão a seguinte nomenclatura: stf + [NOME DA TABELA], portando para nosso exemplo obtemos: stfFUNC, conforme o código a seguir:


public class stfFUNC

{

}

Na tabela FUNC possuímos os seguintes campos: CD_FUNC, NM_FUNC, DS_FUNC_DTLHE. Os nomes das variáveis obedecerão a seguinte nomenclatura: m + [NOME DO CAMPO], conforme o código a seguir:


private int mCD_FUNC;

private string mNM_FUNC;

private string mDS_FUNC_DTLHE;

As propriedades possuirão o mesmo nome do campo, logo teremos um código semelhante à Listagem 11.

Listagem 11. Declaração das propriedades da classe stfFUNC.

public int CD_FUNC

{

  get {
    return mCD_FUNC;
  }

  set {
    mCD_FUNC = value;
  }

}

public string NM_FUNC

{

  get {
    return mNM_FUNC;
  }

  set {
    mNM_FUNC = value;
  }

}

public string DS_FUNC_DTLHE

{

  get {
    return mDS_FUNC_DTLHE;
  }

  set {
    mDS_FUNC_DTLHE = value;
  }

}

O construtor da classe colocará um valor padrão em cada propriedade, assim saberemos quando um valor foi informado a elas, utilizando o código a seguir:


public stfFUNC()

{

  this.CD_FUNC = -1;

  this.NM_FUNC = "";

  this.DS_FUNC_DTLHE = "";

}

DAL

Mencionamos também, que teríamos as classes de persistência que serão responsáveis pela execução de scripts SQL de manipulação de dados. As classes DAL, ficarão dentro do mesmo arquivo de código da classe STF, em nosso exemplo esse arquivo é o clsFUNC.cs.

Padronizaremos o nome das classes de acordo com a nomenclatura: dal + [NOME DA TABELA], portanto em nosso exemplo obteremos o seguinte resultado:


public class dalFUNC

{

}

Somente duas propriedades serão criadas nessa classe, onde uma será somente leitura para retornar possíveis mensagens para a classe de negócio e a outra para receber a classe de conexão DataBaseConn, como podemos ver na Listagem 12.

Listagem 12. Propriedades da classe dalFUNC.

private string mStrMessage;

private DataBaseConn mObjConnection;

public string getMessage()

{

  return mStrMessage;

}

public DataBaseConn Connection

{

  get {
    return mObjConnection;
  }

  set {
    mObjConnection = value;
  }

}

O método privado TheMax retornará o maior código referente à chave primária da tabela, para que seja possível acrescentar um o código e incluirmos o novo registro. Codifique-o conforme a Listagem 13.

Listagem 13. Método TheMax.

private int TheMax()

{

  StringBuilder lStrSQL = new StringBuilder();

  IDataReader lRst;

  lStrSQL.Append("select Max(CD_FUNC) From FUNC");

  lRst = mObjConnection.ExecuteReader(

    lStrSQL.ToString());

  int lIntMax = 0;

  if (lRst.Read())

  {

    if (!lRst.IsDBNull(0))

    {

      lIntMax = lRst.GetInt32(0);

    }

  }

  lRst.Close();

  lRst.Dispose();

  lRst = null;

  return lIntMax;

}

Para padronizarmos algumas mensagens que podem ser retornadas para a classe de negócio, utilizaremos o método privado setMessage. Esse método receberá como parâmetro um inteiro referente à ação que está sendo executada pela classe DAL e um inteiro longo referente ao número de registros que foram afetados pela execução do SQL. Veja o código na Listagem 14.

Listagem 14. Método setMessage.

protected void setMessage(int pAcao, long pRows)

{

  /*

  * 0 = Insert

  * 1 = Update

  * 2 = Delete

  * 3 = Refresh

  */

  mStrMessage = "";

  switch (pAcao)

  {

  case 0:

    if (pRows == 0)

    {

      mStrMessage = "Nenhum registro foi salvo.";

    } else

    {

      if (pRows == 1)

      {

        mStrMessage = "Foi salvo " + pRows +

          " registro.";

      } else

      {

        mStrMessage = "Foram salvos " + pRows +

          " registros.";

      }

    }

    break;

  case 1:

    if (pRows == 0)

    {

      mStrMessage =

        "Nenhum registro foi atualizado.";

    } else

    {

      if (pRows == 1)

      {

        mStrMessage = "Foi atualizado " + pRows +

          " registro.";

      } else

      {

        mStrMessage = "Foram atualizados " + pRows +

          " registros.";

      }

    }

    break;

  case 2:

    if (pRows == 0)

    {

      mStrMessage = "Nenhum registro foi excluído.";

    } else

    {

      if (pRows == 1)

      {

        mStrMessage = "Foi excluído " + pRows +

          " registro.";

      } else

      {

        mStrMessage = "Foram excluídos " + pRows +

          " registros.";

      }

    }

    break;

  } //switch

}

O insert, receberá como parâmetro a classe STF referente à tabela. Antes da execução do SQL de Insert o próximo código (chave primária) é informado para a classe STF, como vemos na Listagem 15.

Listagem 15. Método insert da classe dalFUNC.

public void insert(stfFUNC pSTF)

{

  try

  {

    StringBuilder lStrSQL = new StringBuilder();

    pSTF.CD_FUNC = this.TheMax() + 1;

    lStrSQL.Append("Insert Into FUNC");

    lStrSQL.Append(" (");

    lStrSQL.Append(" CD_FUNC,");

    lStrSQL.Append(" NM_FUNC,");

    lStrSQL.Append(" DS_FUNC_DTLHE");

    lStrSQL.Append(" )");

    lStrSQL.Append(" Values");

    lStrSQL.Append(" (");

    lStrSQL.Append(" " + pSTF.CD_FUNC + ",");

    lStrSQL.Append(" '" + pSTF.NM_FUNC + "',");

    lStrSQL.Append(" '" + pSTF.DS_FUNC_DTLHE + "'");

    lStrSQL.Append(" )");

    long lLngRows = mObjConnection.ExecuteSQL(

      lStrSQL.ToString());

    this.setMessage(0, lLngRows);

  } catch (Exception e)

  {

    throw e;

  }

}

Assim como no insert, o update também receberá como parâmetro a classe STF referente à tabela, conforme a Listagem 16.

Listagem 16. Método update da classe dalFUNC.

public void update(stfFUNC pSTF)

{

  try

  {

    StringBuilder lStrSQL = new StringBuilder();

    lStrSQL.Append("Update FUNC Set");

    lStrSQL.Append(" NM_FUNC = '" + pSTF.NM_FUNC +

      "',");

    lStrSQL.Append(" DS_FUNC_DTLHE = '" +

      pSTF.DS_FUNC_DTLHE + "'");

    lStrSQL.Append(" Where");

    lStrSQL.Append(" CD_FUNC = " + pSTF.CD_FUNC + "");

    long lLngRows = mObjConnection.ExecuteSQL(

      lStrSQL.ToString());

    this.setMessage(1, lLngRows);

  } catch (Exception e)

  {

    throw e;

  }

}

O delete, também recebe como parâmetro a classe STF referente à tabela. Porém, as propriedades com valor podem ser somente as referentes à chave primária da tabela. Uma outra prática muito utilizada para o delete é a utilização direta dos campos, referentes à chave primária, como parâmetros (Listagem 17).

Listagem 17. Método delete da classe dalFUNC.

public void delete(stfFUNC pSTF)

{

  try

  {

    StringBuilder lStrSQL = new StringBuilder();

    lStrSQL.Append("Delete From FUNC");

    lStrSQL.Append(" Where");

    lStrSQL.Append(" CD_FUNC = " + pSTF.CD_FUNC + "");

    long lLngRows = mObjConnection.ExecuteSQL(

      lStrSQL.ToString());

    this.setMessage(2, lLngRows);

  }

  catch (Exception e)

  {

    throw e;

  }

}

Para concluirmos a classe DAL, os últimos métodos a serem implementados são responsáveis por retornar os registros do banco de dados em um IDataReader, portanto serão chamados de refresh.

Nota: Um IDataReader é uma interface do ADO.NET responsável por representar um conjunto de dados, mais especificamente um resultset. É a maneira mais rápida de ler dados de um BD, pois é somente-leitura e realiza uma navegação nos registros unidirecional, sem fazer cache, ideal para o nosso exemplo.

Um dos métodos possuirá como parâmetro todos os campos referente à tabela e também um parâmetro chamado pOrder que servirá para ordenar a consulta. O outro método não possuirá parâmetros e fará chamada ao método com parâmetros, como podemos ver na Listagem 18.

Listagem 18. Métodos refresh da classe dalFUNC.

public IDataReader refresh()

{

  return refresh(-1, "", "", "");

}

public IDataReader refresh(int pCD_FUNC,

  string pNM_FUNC, string pDS_FUNC_DTLHE,

  string pORDER)

{

  StringBuilder lStrSQL = new StringBuilder();

  lStrSQL.Append("Select");

  lStrSQL.Append(" FUNC.CD_FUNC,");

  lStrSQL.Append(" FUNC.NM_FUNC,");

  lStrSQL.Append(" FUNC.DS_FUNC_DTLHE");

  lStrSQL.Append(" From FUNC");

  if (pCD_FUNC >= 0)

  {

    if (lStrSQL.ToString().IndexOf("WHERE") <= 0)

    {

      lStrSQL.Append(" WHERE");

    } else

    {

      lStrSQL.Append(" And");

    }

    lStrSQL.Append(" FUNC.CD_FUNC = " +

      pCD_FUNC + "");

  }

  if (!pNM_FUNC.Trim().Equals(""))

  {

    if (lStrSQL.ToString().IndexOf("WHERE") <= 0)

    {

      lStrSQL.Append(" WHERE");

    } else

    {

      lStrSQL.Append(" And");

    }

    lStrSQL.Append(" FUNC.NM_FUNC = '" +

      pNM_FUNC + "'");

  }

  if (!pDS_FUNC_DTLHE.Trim().Equals(""))

  {

    if (lStrSQL.ToString().IndexOf("WHERE") <= 0)

    {

      lStrSQL.Append(" WHERE");

    } else

    {

      lStrSQL.Append(" And");

    }

    lStrSQL.Append(" FUNC.DS_FUNC_DTLHE = '" +

      pDS_FUNC_DTLHE + "'");

  }

  if (!pORDER.Trim().Equals(""))

  {

    lStrSQL.Append(" Order By " + pORDER + "");

  }

  //Abre o DataReader para retorná-lo

  IDataReader lRst = mObjConnection.ExecuteReader(

    lStrSQL.ToString());

  return lRst;

}

Durante o desenvolvimento deste artigo, fizemos à utilização de diversas práticas que caracterizam a Programação Orientada a Objetos. Caso você não tenha notado, não se preocupe, pois elas realmente fazem parte do nosso dia a dia e muitas vezes nem percebemos quando estamos utilizando-as.

Quando ainda estávamos criando os projetos, definimos que teríamos um projeto para as classes de negócio, persistência e estado. Com isso, encapsulamos em apenas um arquivo DLL, após a compilação do projeto, todas as regras da aplicação. Separamos, também, as regas de negócio da persistência de dados (DAL).

BSN

Nas classes de negócio, chamadas de BSN, temos métodos públicos para validar os dados, salvar, excluir, pesquisar e preencher (DropDownList e GridView). Ainda nestas classes, possuímos um objeto da classe DAL referente à tabela e propriedades para receber a conexão, retornar os Itens encontrados e mensagens.

Assim como a classe DAL fica dentro do mesmo arquivo de código da classe STF, com a classe BSN não podia ser diferente.

Em nosso exemplo esse arquivo é clsFUNC.cs. Padronizaremos o nome das classes de acordo com a nomenclatura: bsn + [NOME DA TABELA], portanto, em nosso exemplo iremos obter o seguinte resultado: bsnFUNC.


public class bsnFUNC

{

}

As variáveis, como nas classes anteriores, serão privadas. Portanto teremos propriedades para permitir o acesso, ao valor de cada uma, pela interface do usuário.

Como mencionamos anteriormente, teremos uma coleção da classe STF e utilizaremos Generics para que nossa coleção tenha um tipo definido. Caso você não possua domínio sobre este assunto sugiro que faça a leitura do artigo “Saiba o que são, para que servem e como trabalhar com Generics” publicado na edição número 33 desta revista.


private dalFUNC mDAL = new dalFUNC();

private string mStrMessage;

private List mColItens;

private DataBaseConn mObjConnection;

public string Message

{

  get {
    return mStrMessage;
  }

}

public List Itens

{

  get {
    return mColItens;
  }

}

public DataBaseConn Connection

{

  get {
    return mObjConnection;
  }

  set

  {

    mObjConnection = value;

    mDAL.Connection = mObjConnection;

  }

}

A validação dos dados, imputados pelo usuário, será realizada através do método validarDados desta classe. Este método recebe como parâmetro uma classe STF e realiza a verificação dos dados, atribuindo as inconsistências na variável mStrMessage.


public bool validarDados(stfFUNC pSTF)

{

  string lStrMessage = "";

  if (pSTF.NM_FUNC == null || pSTF.NM_FUNC.Equals(string.Empty))

  {

    lStrMessage += "- O campo: Nome é de preenchimento obrigatório.\\n";

  }

  if (lStrMessage.Trim().Equals(""))

  {

    return true;

  } else

  {

    this.mStrMessage = "Dados abaixo são obrigatórios ou estão inválidos: \\n\\n" + lStrMessage;

    return false;

  }

}

Suponhamos que estivéssemos implementando a classe de negócio referente a atividades do projeto. Sabemos que uma atividade só pode ser concluída caso tenham tarefas relacionadas, pois as datas de início real e término real são, respectivamente, a menor e maior data das tarefas relacionadas. Por exemplo, antes de finalizar uma atividade do projeto, poderíamos utilizar o método validarDados para verificar a existência de tarefas para a atividade do projeto em questão.

O método salvar é responsável pela inclusão de um novo registro e também pela alteração de um registro editado. Para distinguir as duas ações possuiremos um parâmetro inteiro que servirá de referência. Outro parâmetro do método é a classe STF referente à tabela.


public bool salvar(stfFUNC pSTF, int pAction)

{

  //Valida os dados

  if (validarDados(pSTF))

  {

    if (pAction == 0)

    {

      try

      {

        mDAL.insert(pSTF);

        this.mStrMessage = mDAL.getMessage();

        return true;

      } catch

      {

        this.mStrMessage = "Erro ao incluir";

        return false;

      }

    } else

    {

      try

      {

        mDAL.update(pSTF);

        this.mStrMessage = mDAL.getMessage();

        return true;

      } catch

      {

        this.mStrMessage = "Erro ao atualizar";

        return false;

      }

    }

  } else

  {

    return false;

  }

}

O método excluir possui como parâmetro a classe STF referente à tabela, porém somente é necessário possuir valor nos campos referentes à chave primária. Assim como no método delete da classe DAL, podem ser utilizados os campos diretamente como parâmetro do método.


public bool excluir(stfFUNC pSTF)

{

    try

    {

        mDAL.delete(pSTF);

        this.mStrMessage = mDAL.getMessage();

        return true;

    }

    catch

    {

        this.mStrMessage = "Erro ao excluir";

        return false;

    }

}

O método pesquisar é responsável pelo preenchimento da coleção de classes STF. Posteriormente essa coleção será utilizada para preencher os controles GridView e DropDownList. Assim como o método refresh da classe DAL, ele possuirá duas implementações.


public bool pesquisar()

{

  return pesquisar(-1, "", "");

}

public bool pesquisar(int pCD_FUNC, string pNM_FUNC, string pDS_FUNC_DTLHE)

{

  IDataReader lRst;

  try

  {

    mDAL.Connection = mObjConnection;

    lRst = mDAL.refresh(pCD_FUNC, pNM_FUNC, pDS_FUNC_DTLHE, string.Empty);

    //preenche a coleção com os registros

    mColItens = new List();

    while (lRst.Read())

    {

      stfFUNC lStf = new stfFUNC();

      lStf.CD_FUNC = Convert.ToInt32(lRst.IsDBNull(0) ? 0 : lRst.GetInt32(0));

      lStf.NM_FUNC = Convert.ToString(lRst.IsDBNull(1) ? "" : lRst.GetString(1));

      lStf.DS_FUNC_DTLHE = Convert.ToString(lRst.IsDBNull(2) ? "" : lRst.GetString(2));

      this.mColItens.Add(lStf);

    }

    lRst.Close();

    lRst.Dispose();

    this.mStrMessage = mDAL.getMessage();

    return true;

  } catch

  {

    this.mStrMessage = "Erro ao pesquisar";

    return false;

  }

}

Para finalizarmos a classe de negócios (BSN) iremos descrever a implementação dos métodos de preenchimento de controles. Os controles estão localizados na interface com o usuário e serão enviados para a classe através de parâmetros do método.

Inicialmente estamos descrevendo o preenchimento dos controles DropDownList e GridView, porém você não terá dificuldades para realizar o preenchimento de outros controles.

No preenchimento do DropDownList possuiremos dois métodos, sendo que um deles possuirá um parâmetro que será incluído no inicio da listagem dos itens. O ponto chave para este método é setar as propriedades DataValueField e DataTextField respectivamente com as propriedades, da classe STF, referente a chave primária e referente ao nome do item. Com isso, o controle saberá que propriedade terá que acessar para buscar a informação.

Outro ponto relevante é não esquecer de setar a coleção de STF como DataSource e chamar o método DataBind, para que automaticamente os itens da coleção sejam carregados no controle.


public bool preencher(System.Web.UI.WebControls.DropDownList pDropDownList)

{

    return this.preencher(pDropDownList, "");

}

 

public bool preencher(System.Web.UI.WebControls.DropDownList pDropDownList, String pFristItem)

{

    try

    {

        pDropDownList.Items.Clear();

 

        pDropDownList.DataValueField = "CD_FUNC";

        pDropDownList.DataTextField = "NM_FUNC";

 

        pDropDownList.DataSource = this.Itens;

        pDropDownList.DataBind();

 

        if (!pFristItem.Trim().Equals(""))

        {

            pDropDownList.Items.Insert(0, new System.Web.UI.WebControls.ListItem(pFristItem, "-1"));

            pDropDownList.SelectedIndex = 0;

        }

 

        return true;

    }

    catch

    {

        this.mStrMessage = "Erro ao preencher";

        return false;

    }

}

Para o preenchimento do GridView possuiremos um único metodo, porém alguns pontos são extremamente relevante, são eles:

  • Precisamos informar para o controle quais são os campos que serão apresentados.

    • Para resolver esse problema setaremos a propriedade AutoGenerateColumns para false e criaremos um ArrayList onde seus itens serão as colunas.

      Cada item, no ArrayList, será vetor string com três posições e as posições serão respectivamente o nome da propriedade da classe STF, o título para a coluna e o tamanho da coluna.

  • Precisamos informar qual, ou quais, propriedades da classe STF será a chave primaria.

    • Esse ponto será resolvido setando-se a propriedade DataKeyNames que recebe um vetor string com o nome de cada propriedade, da classe STF, referente a chave primária.

O código necessário para suprir essas necessidas está descrito abaixo:


public bool preencher(System.Web.UI.WebControls.GridView pGridView)

{

  try

  {

    //Colunas e Titulos

    ArrayList arrColumns = new ArrayList();

    arrColumns.Add(new string[3] {
      "CD_FUNC",
      "Código",
      "150px"
    });

    arrColumns.Add(new string[3] {
      "NM_FUNC",
      "Descrição",
      ""
    });

    pGridView.AutoGenerateColumns = false;

    pGridView.Columns.Clear();

    //Informa as chaves primárias para o GridView

    pGridView.DataKeyNames = new string[1] {
      "CD_FUNC"
    };

    //Botão para selecionar o registro.

    System.Web.UI.WebControls.CommandField cmf = new System.Web.UI.WebControls.CommandField();

    cmf.HeaderStyle.Width = new System.Web.UI.WebControls.Unit(20, System.Web.UI.WebControls.UnitType.Pixel);

    cmf.HeaderStyle.Wrap = false;

    cmf.ShowSelectButton = true;

    cmf.SelectText = "imgs/btn_editar.gif";

    cmf.Visible = true;

    pGridView.Columns.Add(cmf);

    for (int lInt = 0; lInt <= arrColumns.Count - 1; lInt++)

    {

      string[] lStrCol = (string[]) arrColumns[lInt];

      System.Web.UI.WebControls.BoundField dcf = new System.Web.UI.WebControls.BoundField();

      dcf.DataField = lStrCol[0];

      dcf.HeaderText = lStrCol[1];

      if (!lStrCol[2].Trim().Equals(""))

      {

        dcf.HeaderStyle.Width = new System.Web.UI.WebControls.Unit(lStrCol[2]);

        dcf.HeaderStyle.Wrap = false;

      } else

      {

        dcf.HeaderStyle.Width = new System.Web.UI.WebControls.Unit("100%");

      }

      //Alinhamentos

      dcf.HeaderStyle.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Left;

      dcf.ItemStyle.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Left;

      dcf.FooterStyle.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Left;

      pGridView.Columns.Add(dcf);

    }

    pGridView.DataSource = this.Itens;

    pGridView.DataBind();

    return true;

  } catch

  {

    this.mStrMessage = "Erro ao preencher";

    return false;

  }

}

Construindo a camada de apresentação

Agora que possuímos a master page, a ToolBar, as classes de persistência, as classes negócio e as classes de estado implementadas é hora de darmos início a construção da camada de apresentação.

Primeiramente vamos estabelecer uma regra, todas as páginas herdarão da master page. Assim manteremos uma padronização de leiaute e quando for necessário alterá-lo poderemos fazer somente em um lugar.

A aplicação possuirá uma página chamada Default.aspx que, por exemplo, poderá servir para apresentar ao usuário os projetos e suas respectivas atividades que estão próximas de serem iniciadas ou as atividades já vencidas.

Exibindo uma listagem dos registros

No Solution Explorer, clique com o botão direito sobre o projeto Colab.UI e depois no item Add New Item... .Na janela selecione (Figura 13) o template Web Form e no campo Name informe o nome Display.aspx, selecione as opções: “Place code in separate file” e “Select master page”.

Esta página servirá para exibir uma listagem dos registros referentes a opção selecionada no menu principal.

Adicionando a página Display.aspx ao projeto
Figura 13. Adicionando a página Display.aspx ao projeto.

Nesta página possuiremos dois controles, o User Control wucToolBar que desenvolvemos anteriormente e um GridView. Estes controles serão respectivamente chamados de tBar e grd.

Para o ToolBar iremos implementar o evento NewClick, para direcionarmos para a página referente a opção selecionada no menu principal. No GridView implementaremos os eventos: PageIndexChanging que será responsável por atualizar a listagem com os dados referente a página for selecionada e SelectedIndexChanged que chamará a página referente ao item selecionado na listagem, porém já preenchendo os controles com os dados do registro selecionado. A Listagem 10, apresenta o código HTML necessário para a página Display.aspx e a Figura 14 mostra como deve ficar o leiaute da página após a implementação do código.

Listagem 10. Código HTML da página Display.aspx.

<%@
   Page Language="C#" MasterPageFile="~/mpgBasica.master"
   AutoEventWireup="true" CodeFile="Display.aspx.cs" Inherits="Display"
   Title="" %><%@ Register Src="wucToolBar.ascx" TagName="wucToolBar" TagPrefix="uc1" %>
<asp:Content id=cntDisplay Runat="Server" ContentPlaceHolderID="cphCentro">
   <asp:UpdatePanel id=uppDisplay runat="server">
      <CONTENTTEMPLATE>
         <xml:namespace prefix="uc1" />
         <uc1:wucToolBar id="tBar" runat="server" SaveIsVisible="false" NewIsVisible="true" DeleteIsVisible="false" BackIsVisible="false" OnNewClick="tBar_OnNewClick" >
         </uc1:wucToolBar>
         <asp:GridView id="grd" runat="server" Width="100%" HorizontalAlign="Center" CellPadding="3" ForeColor="Black" GridLines="Horizontal" ShowFooter="True" AllowPaging="True" PageSize="20" OnPageIndexChanging="grd_PageIndexChanging" OnSelectedIndexChanged="grd_SelectedIndexChanged">
            <FOOTERSTYLE ForeColor="White" Font-Size="8pt" Font-Names="Verdana,Arial,Helvetica,sans-serif" Font-Bold="True" BackColor="#5D7B9D" />
            <ROWSTYLE ForeColor="#333333" Font-Size="8pt" Font-Names="Verdana,Arial,Helvetica,sans-serif" BackColor="#F7F6F3" />
            <EDITROWSTYLE BackColor="#999999" />
            <SELECTEDROWSTYLE ForeColor="#333333" Font-Size="8pt" Font-Names="Verdana,Arial,Helvetica,sans-serif" Font-Bold="True" BackColor="#E2DED6" />
            <PAGERSTYLE HorizontalAlign="Center" ForeColor="White" BackColor="#284775" Wrap="True" VerticalAlign="Middle" Font-Strikeout="False" />
            <HEADERSTYLE HorizontalAlign="Left" ForeColor="White" Font-Size="8pt" Font-Names="Verdana,Arial,Helvetica,sans-serif" Font-Bold="True" BackColor="#5D7B9D" Wrap="False" VerticalAlign="Middle"             />
            <ALTERNATINGROWSTYLE ForeColor="#284775" Font-Size="8pt" Font-Names="Verdana,Arial,Helvetica,sans-serif" BackColor="White" />
         </asp:GridView>
      </CONTENTTEMPLATE>
   </asp:UpdatePanel>
</asp:Content>
</EDITROWSTYLE>
</ROWSTYLE>
</FOOTERSTYLE>
</asp:gridview>
</CONTENTTEMPLATE>
</asp:updatepanel>
</asp:content>
Leiaute da página Display.aspx em design mode.
Figura 14. Leiaute da página Display.aspx em design mode.

Falamos que a display.aspx será responsável por exibir a listagem dos dados e transferirá para a página de edição de acordo com a opção selecionada no menu principal. Para fazermos isso será necessário recuperarmos o valor da QueryString, enviada pelo menu principal para a pagina display.aspx, e armazená-lo em uma variável.

Resolvemos este problema implementando o evento Page_Load da página. Este evento fará a recuperação do valor da QueryString e armazenará na variável mStrOption que pertence ao escopo da classe.

Um método, chamado BindGrid, será implementado para instanciar um objeto da classe BSN referente a opção selecionada e fazer chamada aos métodos pesquisar e preencher.

Teremos ainda, dois métodos responsáveis por fazer a chamada da página de edição da opção selecionada, a saber: tBar_OnNewClick, grd_SelectedIndexChanged. Estes métodos criarão variáveis na sessão do usuário que conterá os valores (chave primária) referentes ao registro que está sendo alterado, isso no caso do método SelectedIndexChanged.

Agora que todos as ações da página (display.aspx) foram descritas apresentamos o seu respectivo código na Listagem 11.

Listagem 11. Código para a pagina Display.aspx.

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using ColabProject.Util;

using ColabProject.Persistence.clsFUNC;
using ColabProject.Persistence.clsCLIENT;
using ColabProject.Persistence.clsCOLAB;
using ColabProject.Persistence.clsPROJT;
using ColabProject.Persistence.clsTARFS;

public partial class Display: System.Web.UI.Page {
  String mStrOption;

  DataBaseConn mConn;

  protected void Page_Load(object sender, EventArgs e) {
    try {
      mStrOption = Request.QueryString["trv"];

      tBar.NewIsVisible = true;
      tBar.SaveIsVisible = false;
      tBar.DeleteIsVisible = false;
      tBar.BackIsVisible = false;

      if (!Page.IsPostBack) {
        this.BindGrid();
      }
    } catch (Exception ex) {}
  }

  private void BindGrid() {
    mConn = new DataBaseConn();

    if (mStrOption.Trim().Equals("FUNC")) {
      tBar.PageHeader = "Tipos de Funções";

      bsnFUNC lBsnFUNC = new bsnFUNC();
      lBsnFUNC.Connection = mConn;
      lBsnFUNC.pesquisar();
      lBsnFUNC.preencher(grd);
    } else if (mStrOption.Trim().Equals("CLIENT")) {
      tBar.PageHeader = "Clientes";

      bsnCLIENT lBsnCLIENT = new bsnCLIENT();
      lBsnCLIENT.Connection = mConn;
      lBsnCLIENT.pesquisar();
      lBsnCLIENT.preencher(grd);
    } else if (mStrOption.Trim().Equals("COLAB")) {
      tBar.PageHeader = "Colaboradores";

      bsnCOLAB lBsnCOLAB = new bsnCOLAB();
      lBsnCOLAB.Connection = mConn;
      lBsnCOLAB.pesquisar();
      lBsnCOLAB.preencher(grd);
    } else if (mStrOption.Trim().Equals("PROJT")) {
      tBar.PageHeader = "Projetos";

      bsnPROJT lBsnPROJT = new bsnPROJT();
      lBsnPROJT.Connection = mConn;
      lBsnPROJT.pesquisar();
      lBsnPROJT.preencher(grd);
    } else if (mStrOption.Trim().Equals("TARFS")) {
      tBar.PageHeader = "Tarefas";

      bsnTARFS lBsnTARFS = new bsnTARFS();
      lBsnTARFS.Connection = mConn;
      lBsnTARFS.pesquisar();
      lBsnTARFS.preencher(grd);
    }

    mConn.CloseConnection();
  }

  protected void tBar_OnNewClick(object sender, EventArgs e) {
    if (mStrOption.Trim().Equals("FUNC")) {
      Session.Remove("CD_FUNC");
      Session.Add("CD_FUNC", "-1");
      Response.Redirect("cad_tipo_funcao.aspx", true);
    } else if (mStrOption.Trim().Equals("CLIENT")) {
      Session.Remove("CLIENT");
      Session.Add("CLIENT", "-1");
      Response.Redirect("cad_pessoa_cliente.aspx", true);
    } else if (mStrOption.Trim().Equals("COLAB")) {
      Session.Remove("CD_COLAB");
      Session.Add("CD_COLAB", "-1");
      Response.Redirect("cad_pessoa_colaborador.aspx", true);
    } else if (mStrOption.Trim().Equals("PROJT")) {
      Session.Remove("CD_PROJT");
      Session.Add("CD_PROJT", "-1");
      Response.Redirect("cad_projetos.aspx", true);
    } else if (mStrOption.Trim().Equals("TARFS")) {
      Session.Remove("CD_TARFS");
      Session.Add("CD_TARFS", "-1");
      Response.Redirect("cad_tarefas.aspx", true);
    }

  }

  protected void grd_SelectedIndexChanged(object sender, EventArgs e) {
    if (mStrOption.Trim().Equals("FUNC")) {
      Session.Remove("CD_FUNC");
      Session.Add("CD_FUNC", grd.SelectedValue);
      Response.Redirect("cad_tipo_funcao.aspx", true);
    } else if (mStrOption.Trim().Equals("CLIENT")) {
      Session.Remove("CD_CLIENT");
      Session.Add("CD_CLIENT", grd.SelectedValue);
      Response.Redirect("cad_pessoa_cliente.aspx", true);
    } else if (mStrOption.Trim().Equals("COLAB")) {
      Session.Remove("CD_COLAB");
      Session.Add("CD_COLAB", grd.SelectedValue);
      Response.Redirect("cad_pessoa_colaborador.aspx", true);
    } else if (mStrOption.Trim().Equals("PROJT")) {
      Session.Remove("CD_PROJT");
      Session.Add("CD_PROJT", grd.SelectedValue);
      Response.Redirect("cad_projetos.aspx", true);
    } else if (mStrOption.Trim().Equals("TARFS")) {
      Session.Remove("CD_TARFS");
      Session.Add("CD_TARFS", grd.SelectedValue);
      Response.Redirect("cad_tarefas.aspx", true);
    }
  }

  protected void grd_PageIndexChanging(object sender, GridViewPageEventArgs e) {
    grd.PageIndex = e.NewPageIndex;
    this.BindGrid();
  }

}

A Figura 15 mostra aplicação em execução, mostrando a listagem de tipos de funções. Como ainda não desenvolvemos as telas de cadastros os registros apresentados na listagem foram incluídos diretamente no banco de dados.

Aplicação em execução, exibindo os tipos de funções
Figura 15. Aplicação em execução, exibindo os tipos de funções.

Inclusão/Visualização/Alteração de um registro

Da mesma forma que fizemos para a confecção das classes STF, DAL e BSN iremos apresentar somente a implementação de uma das telas de cadastro. Para facilitar o entendimento de todos, vamos dar continuidade fazendo a tela de cadastro das funções que um colaborador pode exercer.

No Solution Explorer, clique com o botão direito sobre o projeto Colab.UI e depois no item Add New Item... .Na janela selecione, Figura 13, o template Web Form e no campo Name informe o nome “cad_tipo_funcao.aspx”, selecione as opções: “Place code in separate file” e “Select master page”.

Nesta página, também, incluiremos o User Control wucToolBar.ascx que desenvolvemos anteriormente e desta vez realizaremos a implementação de todos os seus eventos: NewClick, SaveClick, DeleteClick e BackClick. A Listagem 12 apresenta todo o código HTML necessário e a Figura 16 mostra o leiaute da página.

Leiaute da página cad_tipo_funcao.aspx em design mode
Figura 16. Leiaute da página cad_tipo_funcao.aspx em design mode.
Listagem 12. Código HTML da página cad_tipo_funcao.aspx.

<%@
Page Language="C#" MasterPageFile="~/mpgBasica.master"
AutoEventWireup="true" CodeFile="cad_tipo_funcao.aspx.cs"
Inherits="cad_tipo_funcao" Title="Untitled Page" %><%@ Register Src="wucToolBar.ascx" TagName="wucToolBar" TagPrefix="uc1" %> <asp:Content id=cttTipoFuncao Runat="Server" ContentPlaceHolderID="cphCentro">
  <uc1:wucToolBar id=tBar runat="server" OnNewClick="tBar_NewClick" OnBackClick="tBar_BackClick" OnDeleteClick="tBar_DeleteClick" OnSaveClick="tBar_SaveClick"></uc1:wucToolBar>
  <TABLE cellSpacing=0 cellPadding=0 border=0>
    <TBODY>
      <TR>
        <TD>
          <asp:Label id=Label1 runat="server" Text="Código:"></asp:Label>
        </TD>
        <TD noWrap width=5></TD>
        <TD>
          <asp:Label id=Label2 runat="server" Text="Nome:"></asp:Label>
        </TD>
      </TR>
      <TR>
        <TD>
          <asp:TextBox id=txtCD_FUNC runat="server" Width="65px" BackColor="Info" Text="" ReadOnly="True" ToolTip="Código
da Função"></asp:TextBox>
        </TD>
        <TD></TD>
        <TD>
          <asp:TextBox id=txtNM_FUNC runat="server" Width="300px" Text="" ToolTip="Nome da
Função" MaxLength="30"></asp:TextBox>
        </TD>
      </TR>
      <TR>
        <TD noWrap colSpan=3 height=5></TD>
      </TR>
      <TR>
        <TD colSpan=3>
          <asp:Label id=Label3 runat="server" Text="Descrição:"></asp:Label>
        </TD>
      </TR>
      <TR>
        <TD colSpan=3>
          <asp:TextBox id=txtDS_FUNC_DTLHE runat="server" Width="370px" Text="" ToolTip="Descrição Detalhada da Função" MaxLength="30" TextMode="MultiLine" Rows="3"></asp:TextBox>
        </TD>
      </TR>
    </TBODY>
  </TABLE>
</asp:Content>

No evento Page_Load, iremos verificar se a variável de sessão possui o valor referente a um registro. Se esta verificação for verdadeira, instanciamos a classe de negócio e o método pesquisar será chamado em seguida passando o valor como parâmetro e se apenas um resultado for encontrado preencheremos os campos da página.


DataBaseConn mConn;

protected void Page_Load(object sender, EventArgs e)

{

  tBar.PageHeader = "Tipo de Função";

  if (!Page.IsPostBack)

  {

    int lIntCD_FUNC = int.Parse(Session["CD_FUNC"].ToString());

    if (lIntCD_FUNC != -1)

    {

      bsnFUNC lBsnFUNC = new bsnFUNC();

      mConn = new DataBaseConn();

      lBsnFUNC.Connection = mConn;

      lBsnFUNC.pesquisar(lIntCD_FUNC, "", "");

      if (lBsnFUNC.Itens.Count == 1)

      {

        //Atribuição das informações para os textbox.

        txtCD_FUNC.Text = lBsnFUNC.Itens[0].CD_FUNC.ToString();

        txtNM_FUNC.Text = lBsnFUNC.Itens[0].NM_FUNC.ToString();

        txtDS_FUNC_DTLHE.Text = lBsnFUNC.Itens[0].DS_FUNC_DTLHE.ToString();

      }

      mConn.CloseConnection();

    }

    Session.Remove("CD_FUNC");

  }

}

Neste ponto vale a pena citar, que toda vinculação dos dados com os objetos é feita de forma manual. Porem, poderíamos fazer a utilização do controle ObjectDataSource que é responsável por realizar as funções de manipulação dos dados (select, insert, update, delete) e para isso bastaríamos vincular o objeto referente a classe de negócio é realizar as configurações necessárias. Caso você deseje mais informações sobre como utilizar o ObjectDataSource sugiro que faça a leitura do artigo ObjectDataSource publicado na edição número 33 desta revista.

Quando o usuário clicar sobre o botão Novo da ToolBar o evento NewClick é disparado para página e é tratado no método tBar_NewClick, sendo o responsável por limpar os controles da página.


protected void tBar_NewClick(object sender, EventArgs e)

{

    txtCD_FUNC.Text = "";

    txtNM_FUNC.Text = "";

    txtDS_FUNC_DTLHE.Text = "";

}

Clicando sobre o botão Salvar da ToolBar o evento SaveClick e disparado para página e é tratado pelo método tBar_SaveClick e este por sua vez é responsável por instanciar um objeto da classe STF e setar o valor de suas propriedades. É identificada a ação (inclusão ou alteração) através do valor padrão para os campos referentes à chave primária e o método salvar da classe BSN é chamado com os parâmetros necessários.


protected void tBar_SaveClick(object sender, EventArgs e)

{

  stfFUNC lStfFUNC = new stfFUNC();

  bsnFUNC lBsnFUNC = new bsnFUNC();

  mConn = new DataBaseConn();

  lBsnFUNC.Connection = mConn;

  lStfFUNC.CD_FUNC = (txtCD_FUNC.Text.Trim().Equals("") ? -1 : int.Parse(txtCD_FUNC.Text.Trim()));

  lStfFUNC.NM_FUNC = txtNM_FUNC.Text.Trim();

  lStfFUNC.DS_FUNC_DTLHE = txtDS_FUNC_DTLHE.Text.Trim();

  if (lStfFUNC.CD_FUNC == -1)

  {

    if (lBsnFUNC.salvar(lStfFUNC, 0))

    {

      txtCD_FUNC.Text = lStfFUNC.CD_FUNC.ToString();

    }

  } else

  {

    lBsnFUNC.salvar(lStfFUNC, 1);

  }

  Functions.WriteAlert(this, lBsnFUNC.Message);

  mConn.CloseConnection();

}

Quando o botão clicado é o Excluir o evento DeleteClick é transferido para a página e tratado pelo método tbar_DeleteClick.


protected void tBar_DeleteClick(object sender, EventArgs e)

{

  stfFUNC lStfFUNC = new stfFUNC();

  bsnFUNC lBsnFUNC = new bsnFUNC();

  mConn = new DataBaseConn();

  lBsnFUNC.Connection = mConn;

  lStfFUNC.CD_FUNC = (txtCD_FUNC.Text.Trim().Equals("") ? -1 : int.Parse(txtCD_FUNC.Text.Trim()));

  if (lStfFUNC.CD_FUNC != -1)

  {

    if (lBsnFUNC.excluir(lStfFUNC))

    {

      this.tBar_NewClick(sender, e);

    }

    Functions.WriteAlert(this, lBsnFUNC.Message);

  } else

  {

    Functions.WriteAlert(this, "É necessário que seja selecionado um registro.");

  }

  mConn.CloseConnection();

}

Conforme mencionamos na criação da classe BSN e DAL, informamos somente o valor da propriedade referente à chave primária da tabela e então chamamos o método excluir da classe BSN.

Para concluir, ao se clicar no botão Voltar à listagem será apresentada, ou seja, redirecionaremos para a página Display.aspx informando a QueryString referente ao item em questão.


protected void tBar_BackClick(object sender, EventArgs e)

    {

        Response.Redirect("Display.aspx?trv=FUNC", true);

    }

A Figura 17 mostra aplicação em execução, mostrando a tela de cadastro de um novo tipo de função, após a inclusão de um novo registro.

Aplicação em execução, exibindo a tela de cadastro de tipos de funções
Figura 17. Aplicação em execução, exibindo a tela de cadastro de tipos de funções.

Conclusão

Durante o desenvolvimento deste artigo, fizemos à utilização de diversas práticas que caracterizam a programação orientada a objetos. Caso você não tenha notado, não se preocupe, pois elas realmente fazem parte do nosso dia a dia e muitas vezes não sabemos quando estamos utilizando-as.

Quando ainda estávamos criando os projetos, definimos que teríamos um projeto para as classes de negócio, persistência e estado. Com isso, encapsulamos em apenas um arquivo .DLL, após a compilação do projeto, todas as regras da aplicação. Separamos, também, as regas de negócio (BSN) da persistência de dados (DAL).

Estabelecemos um protocolo, com a utilização das classes de estado cheia (STF), de comunicação entre as camadas da aplicação. Isto facilita muito quando precisamos construir aplicações com diversas camadas de apresentação (UI), seja para o navegador ou para um dispositivo portátil.

Portando uma boa prática, antes do inicio do desenvolvimento de aplicações, é definir algumas coisas básicas do projeto, como por exemplo: o número de camadas, se o aplicativo poderá ser migrado para outra plataforma e/ou acessado por dispositivos portáteis.