Olá! E aí, após modelarmos o banco de dados, construir a biblioteca de classes e uma “quebra de gelo” com a Americanas anunciando GeForce 2 à 1500 Reais em pleno 2009 (Nem Radeon HD 4870 custa isso!), vamos à penúltima parte (na verdade, a última no que se refere a códigos hehe) da construção do nosso Simple PIM (Personal Information Manager), onde iremos construir sua interface web.

A Interface Web será construída em apenas uma página (Single Page Application), sendo TODOS os cadastros implementados através de janelas modais, utilizando mais uma vez a ótima biblioteca jQuery e seu plugin jqModal.

Vamos ver com mais carinho aquele screenshot da solution da segunda parte agora?

Solution Explorer do Simple PIM

Separamos, em diretórios, os arquivos conforme o seu tipo:

Bin: Arquivos compilados, bibliotecas de classe.

css: Folhas de estilo, para a formatação dos elementos da UI.

javascript: Arquivos de script em JavaScript.

imagens: Imagens que farão parte do sistema (neste, particularmente, não coloquei imagens).

Na raíz, encontram-se o web.config, a master page (sim, embora tenha apenas uma página, preferi utilizar master pages neste projeto) e o index.aspx, que é onde tudo acontece.

Vamos primeiro olhar a parte de design:

<%@ Page Language="C#" MasterPageFile="~/principal.master" AutoEventWireup="true"
     CodeFile="index.aspx.cs" Inherits="index" Title="Simple PIM by NeoMatrix Tech" %>
  
 <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
     <div>
         <asp:UpdatePanel runat="server" ID="updGeral">
             <ContentTemplate>
                 <div style="width:100%;text-align:center">
                     <span class="fonte-peq">Escolha o módulo desejado:</span>
                     <asp:DropDownList runat="server" ID="ddlEscolheMod" CssClass="tbx-normal" AutoPostBack="true"
                         OnSelectedIndexChanged="ddlEscolheMod_SelectedIndexChange">
                         <asp:ListItem Value="1" Text="Cadastro de Contatos"></asp:ListItem>
                         <asp:ListItem Value="2" Text="Cadastro de Formas de Contato"></asp:ListItem>
                     </asp:DropDownList>
                 </div>
                 <hr />
                 <asp:Panel runat="server" ID="pnlCadContato">
                     <div style="text-align:center;width:100%;">
                         <asp:Button runat="server" ID="btnFiltrarContato" Text="Filtrar" OnClick="btnFiltrarContato_Click" CssClass="botoes" />
                         <asp:Button runat="server" ID="btnNovoContato" Text="Novo Contato" OnClick="btnNovo_Click" CssClass="botoes" CommandArgument="TContato,0,dvDetContato" /><br />
                     </div>
                     <br />
                     <asp:GridView runat="server" ID="grdContatos" AutoGenerateColumns="false" DataKeyNames="Id" Width="100%">
                     <RowStyle CssClass="grid-normalitem" />
                     <HeaderStyle CssClass="grid-header" />
                         <Columns>
                             <asp:BoundField HeaderText="Nome" DataField="Nome" />
                             <asp:BoundField HeaderText="Sobrenome" DataField="Sobrenome" />
dd/MM/yyyy}" />
                             <asp:BoundField HeaderText="Sexo" DataField="Sexo" />
                             <asp:BoundField HeaderText="Profissão" DataField="Profissao" />
                             <asp:TemplateField>
                                 <ItemTemplate>
                                     <asp:Button runat="server" ID="btnEditarContato" Text="Editar" CommandArgument='<%#Bind("Id","TContato,{0},dvDetContato")%>'
                                         OnClick="btnEditar_Click" CssClass="botoes-grid" />
                                     <asp:Button runat="server" ID="btnExcluirContato" Text="Excluir" CommandArgument='<%#Bind("Id","TContato,{0},dvDetContato")%>' CssClass="botoes-grid" OnClick="btnExcluir_Click" />
                                 </ItemTemplate>
                             </asp:TemplateField>
                         </Columns>
                     </asp:GridView>
                 </asp:Panel>
                 <asp:Panel runat="server" ID="pnlCadFC" Visible="false">
                     <div style="text-align:center;width:100%;">
                         <asp:Button runat="server" ID="btnFiltrarTFC" Text="Filtrar" OnClick="btnFiltrarTFC_Click" CssClass="botoes" />
                         <asp:Button runat="server" ID="btnNovoTFC" Text="Novo Tipo de Forma de Contato" OnClick="btnNovo_Click" CssClass="botoes" CommandArgument="TTpFormaContato,0,dvDetTpFormaContato" /><br />
                     </div>
                     <br />
                     <asp:GridView runat="server" ID="grdTipoFC" AutoGenerateColumns="false" DataKeyNames="Id" Width="100%">
                     <RowStyle CssClass="grid-normalitem" />
                     <HeaderStyle CssClass="grid-header" />
                         <Columns>
                             <asp:BoundField HeaderText="Descrição" DataField="Descricao" />
                             <asp:TemplateField>
                                 <ItemTemplate>
                                     <asp:Button runat="server" ID="btnEditarTFC" Text="Editar" CommandArgument='<%#Bind("Id","TTpFormaContato,{0},dvDetTpFormaContato")%>'
                                         OnClick="btnEditar_Click" CssClass="botoes-grid" />
                                     <asp:Button runat="server" ID="btnExcluirTFC" Text="Excluir" CommandArgument='<%#Bind("Id","TTpFormaContato,{0},dvDetTpFormaContato")%>' CssClass="botoes-grid" OnClick="btnExcluir_Click" />
                                 </ItemTemplate>
                             </asp:TemplateField>
                         </Columns>
                     </asp:GridView>
                 </asp:Panel>
             </ContentTemplate>
         </asp:UpdatePanel>
     </div>
     <div id="dvDetContato" class="jqmWindow" style="overflow: auto; width: 700px; height: 500px;">
         <asp:UpdatePanel runat="server" ID="updContatos">
             <ContentTemplate>
                 <div class="titulos-secao">
                     <span>Detalhes do Contato</span>
                 </div>
                 <div style="width: 80px; float: left">
                     <span class="fonte-peq">Código</span><br />
                     <asp:TextBox ID="tbxTContatoId" runat="server" CssClass="tbx-ro" Width="70px" ReadOnly="true"></asp:TextBox>
                 </div>
                 <div style="width: 250px; float: left">
                     <span class="fonte-peq">Nome</span><br />
                     <asp:TextBox ID="tbxTContatoNome" runat="server" CssClass="tbx-normal" Width="240px"></asp:TextBox>
                 </div>
                 <div style="width: 250px; float: none;">
                     <span class="fonte-peq">Sobrenome</span><br />
                     <asp:TextBox ID="tbxTContatoSobrenome" runat="server" CssClass="tbx-normal" Width="240px"></asp:TextBox>
                 </div>
                 <div style="width: 150px; float: left;">
                     <span class="fonte-peq">Data de Nascimento</span><br />
                     <asp:TextBox ID="tbxTContatoDtNascimento" runat="server" CssClass="tbx-normal" Width="90px"></asp:TextBox>
                 </div>
                 <div style="width: 160px; float: left;">
                     <span class="fonte-peq">SEXO</span><br />
                     <asp:DropDownList ID="ddlTContatoSexo" runat="server" CssClass="tbx-normal" Width="150px">
                         <asp:ListItem Value="M" Text="MASC."></asp:ListItem>
                         <asp:ListItem Value="F" Text="FEM."></asp:ListItem>
                         <asp:ListItem Value="G" Text="GAY"></asp:ListItem>
                         <asp:ListItem Value="L" Text="LÉSBICA"></asp:ListItem>
                         <asp:ListItem Value="B" Text="BISSEXUAL"></asp:ListItem>
                         <asp:ListItem Value="T" Text="TRANSSEXUAL"></asp:ListItem>
                         <asp:ListItem Value="E" Text="METROSSEXUAL"></asp:ListItem>
                     </asp:DropDownList>
                 </div>
                 <div style="width: 160px; float: left;">
                     <span class="fonte-peq">RG</span><br />
                     <asp:TextBox ID="tbxTContatoRg" runat="server" CssClass="tbx-normal" Width="150px"></asp:TextBox>
                 </div>
                 <div style="width: 160px; float: none">
                     <span class="fonte-peq">CPF</span><br />
                     <asp:TextBox ID="tbxTContatoCpf" runat="server" CssClass="tbx-normal" Width="150px"></asp:TextBox>
                 </div>
                 <div style="width: 160px; float: left;">
                     <span class="fonte-peq">CNH</span><br />
                     <asp:TextBox ID="tbxTContatoCnh" runat="server" CssClass="tbx-normal" Width="150px"></asp:TextBox>
                 </div>
                 <div style="width: 250px; float: none">
                     <span class="fonte-peq">PROFISSAO</span><br />
                     <asp:TextBox ID="tbxTContatoProfissao" runat="server" CssClass="tbx-normal" Width="240px"></asp:TextBox>
                 </div>
                 <br />
                 <div style="text-align:center;background-color:Black;width:100%;">
                     <asp:Button runat="server" ID="btnSalvarDetContato" Text="Salvar Contato" OnClick="btnSalvarDetContato_Click" CssClass="botoes" />
                     <asp:Button runat="server" ID="btnFecharDetContato" Text="Fechar" OnClientClick="$dvDetContato.jqmHide(); return false;" CssClass="botoes" />
                 </div>
                 <br />
                 <div style="border: solid 1px black; height: 200px; width: 100%; overflow: auto;">
                     <div class="titulos-secao">
                         <span>Endereços</span>
                     </div>
                     <div style="width:100%;text-align:center;">
                         <asp:Button runat="server" ID="btnNovoEndereco" Text="Novo Endereço" OnClick="btnNovo_Click" CssClass="botoes" CommandArgument="TEndereco,0,dvDetEndereco" />
                     </div>
                     <br />
                     <asp:GridView runat="server" ID="grdTContatoListaEnderecos" AutoGenerateColumns="false"
                         DataKeyNames="Id" Width="100%">
                     <RowStyle CssClass="grid-normalitem" />
                     <HeaderStyle CssClass="grid-header" />
                         <Columns>
                             <asp:BoundField HeaderText="Logradouro" DataField="Logradouro" />
                             <asp:BoundField HeaderText="Numero" DataField="Numero" />
                             <asp:BoundField HeaderText="Cidade" DataField="Cidade" />
                             <asp:BoundField HeaderText="Estado" DataField="Estado" />
                             <asp:TemplateField>
                                 <ItemTemplate>
                                     <asp:Button runat="server" ID="btnEditarEndereco" Text="Editar" CommandArgument='<%#Bind("Id","TEndereco,{0},dvDetEndereco")%>'
                                         OnClick="btnEditar_Click" CssClass="botoes-grid" />
                                     <asp:Button runat="server" ID="btnExcluirEndereco" Text="Excluir" CommandArgument='<%#Bind("Id","TEndereco,{0},dvDetEndereco")%>' CssClass="botoes-grid"  OnClick="btnExcluir_Click"/>
                                 </ItemTemplate>
                             </asp:TemplateField>
                         </Columns>
                     </asp:GridView>
                 </div>
                 <br />
                 <div style="border: solid 1px black; height: 200px; width: 100%; overflow: auto;">
                     <div class="titulos-secao">
                         <span>Formas de Contato</span>
                     </div>
                     <div style="text-align:center;width:100%;">
                     <asp:Button runat="server" ID="btnNovoFormaContato" Text="Nova Forma de Contato"
                         OnClick="btnNovo_Click" CssClass="botoes" CommandArgument="TFormaContato,0,dvDetFormaContato" />
                     </div>    
                     <asp:GridView runat="server" ID="grdTContatoListaFormaContato" AutoGenerateColumns="false"
                         DataKeyNames="Id" Width="100%">
                     <RowStyle CssClass="grid-normalitem" />
                     <HeaderStyle CssClass="grid-header" />
                         <Columns>
                             <asp:BoundField HeaderText="Forma de Contato" DataField="DescricaoFormaContato" />
                             <asp:BoundField HeaderText="Valor" DataField="Valor" />
                             <asp:TemplateField>
                                 <ItemTemplate>
                                     <asp:Button runat="server" ID="btnEditarFC" Text="Editar" CommandArgument='<%#Bind("Id","TFormaContato,{0},dvDetFormaContato")%>'
                                         OnClick="btnEditar_Click" CssClass="botoes-grid" />
                                     <asp:Button runat="server" ID="btnExcluirFC" Text="Excluir" CommandArgument='<%#Bind("Id","TFormaContato,{0},dvFormaDetContato")%>' CssClass="botoes-grid"  OnClick="btnExcluir_Click"/>
                                 </ItemTemplate>
                             </asp:TemplateField>
                         </Columns>
                     </asp:GridView>
                 </div>
             </ContentTemplate>
         </asp:UpdatePanel>
     </div>
     <div id="dvDetEndereco" class="jqmWindow" style="z-index: 3001;">
         <asp:UpdatePanel runat="server" ID="updDetEndereco">
             <ContentTemplate>
                 <div class="titulos-secao">
                     <span>Detalhes do Endereço</span>
                 </div>
                 <!-- A classe TContato é a que irá setar este campo, a PK da tabela de Endereços aponta para ele -->
                 <input type="hidden" id="hdfTContatoId" runat="server" />
                 <div style="width: 160px;">
                     <span class="fonte-peq">ID</span><br />
                     <asp:TextBox ID="tbxTEnderecoId" runat="server" CssClass="tbx-ro" Width="150px" ReadOnly="true"></asp:TextBox>
                 </div>
                 <div style="width: 160px;">
                     <span class="fonte-peq">Tipo de Logradouro</span><br />
                     <asp:DropDownList ID="ddlTEnderecoTipoLogradouro" runat="server" CssClass="tbx-normal"
                         Width="150px">
                         <asp:ListItem Text="Rua" Value="R"></asp:ListItem>
                         <asp:ListItem Text="Avenida" Value="AV"></asp:ListItem>
                         <asp:ListItem Text="Alameda" Value="AL"></asp:ListItem>
                         <asp:ListItem Text="Travessa" Value="TV"></asp:ListItem>
                         <asp:ListItem Text="Beco" Value="B"></asp:ListItem>
                         <asp:ListItem Text="Praça" Value="PC"></asp:ListItem>
                         <asp:ListItem Text="Rodovia" Value="ROD"></asp:ListItem>
                     </asp:DropDownList>
                 </div>
                 <div style="width: 210px; float: left;">
                     <span class="fonte-peq">Logradouro</span><br />
                     <asp:TextBox ID="tbxTEnderecoLogradouro" runat="server" CssClass="tbx-normal" Width="200px"></asp:TextBox>
                 </div>
                 <div style="width: 160px; float: left;">
                     <span class="fonte-peq">Número</span><br />
                     <asp:TextBox ID="tbxTEnderecoNumero" runat="server" CssClass="tbx-normal" Width="150px"></asp:TextBox>
                 </div>
                 <div style="width: 160px; float: none;">
                     <span class="fonte-peq">Complemento</span><br />
                     <asp:TextBox ID="tbxTEnderecoComplemento" runat="server" CssClass="tbx-normal" Width="150px"></asp:TextBox>
                 </div>
                 <div style="width: 160px; float: left;">
                     <span class="fonte-peq">Cep</span><br />
                     <asp:TextBox ID="tbxTEnderecoCep" runat="server" CssClass="tbx-normal" Width="150px"></asp:TextBox>
                 </div>
                 <div style="width: 300px; float: none;">
                     <span class="fonte-peq">Bairro</span><br />
                     <asp:TextBox ID="tbxTEnderecoBairro" runat="server" CssClass="tbx-normal" Width="290px"></asp:TextBox>
                 </div>
                 <div style="width: 260px; float: left;">
                     <span class="fonte-peq">Cidade</span><br />
                     <asp:TextBox ID="tbxTEnderecoCidade" runat="server" CssClass="tbx-normal" Width="250px"></asp:TextBox>
                 </div>
                 <div style="width: 40px; float: none;">
                     <span class="fonte-peq">UF</span><br />
                     <asp:TextBox ID="tbxTEnderecoEstado" runat="server" CssClass="tbx-normal" Width="30px"
                         MaxLength="2"></asp:TextBox>
                 </div>
                 <div style="text-align:center;width:100%;">
                     <asp:Button runat="server" ID="btnSalvaEndereco" Text="Salvar" OnClick="btnSalvaEndereco_Click" CssClass="botoes" />
                     <asp:Button runat="server" ID="btnFecharEndereco" Text="Fechar" OnClientClick="$dvDetEndereco.jqmHide();return false;" CssClass="botoes" />
                 </div>
             </ContentTemplate>
         </asp:UpdatePanel>
     </div>
     <div id="dvDetFormaContato" class="jqmWindow" style="z-index: 3001;">
         <asp:UpdatePanel runat="server" ID="updDetFC">
             <ContentTemplate>
                 <div class="titulos-secao">
                     <span>Detalhes da Forma de Contato</span>
                 </div>
                 <input type="hidden" id="hdfTFormaContatoContatoId" runat="server" />
                 <div style="width: 80px; float: left;">
                     <span class="fonte-peq">ID</span><br />
                     <asp:TextBox ID="tbxTFormaContatoId" runat="server" CssClass="tbx-normal" Width="70px"></asp:TextBox>
                 </div>
                 <div style="width: 210px; float: none;">
                     <span class="fonte-peq">Forma de Contato:</span><br />
                     <asp:DropDownList ID="ddlTFormaContatoTpFormaContatoId" runat="server" CssClass="tbx-normal"
                         Width="200px" DataTextField="Descricao" DataValueField="Id">
                     </asp:DropDownList>
                 </div>
                 <div style="width: 250px;">
                     <span class="fonte-peq">Valor:</span><br />
                     <asp:TextBox ID="tbxTFormaContatoValor" runat="server" CssClass="tbx-normal" Width="240px"></asp:TextBox>
                 </div>
                 <div style="width:100%;text-align:center;">
                     <asp:Button runat="server" ID="btnSalvaFC" Text="Salvar" OnClick="btnSalvaFC_Click" CssClass="botoes" />
                     <asp:Button runat="server" ID="btnFechaFC" Text="Fechar" OnClientClick="$dvDetFormaContato.jqmHide(); return false;" CssClass="botoes" />
                 </div>
             </ContentTemplate>
         </asp:UpdatePanel>
     </div>
     <div id="dvDetTpFormaContato" class="jqmWindow">
         <asp:UpdatePanel runat="server" ID="updDetTpFC">
             <ContentTemplate>
                 <div class="titulos-secao">
                     <span>Detalhes do Tipo de Forma de Contato</span>
                 </div>
                 <div style="width: 90px; float: left;">
                     <span class="fonte-peq">Cód.</span><br />
                     <asp:TextBox ID="tbxTTpFormaContatoId" runat="server" CssClass="tbx-normal" Width="80px"
                         ReadOnly="true"></asp:TextBox>
                 </div>
                 <div style="width: 210px; float: none;">
                     <span class="fonte-peq">Descrição</span><br />
                     <asp:TextBox ID="tbxTTpFormaContatoDescricao" runat="server" CssClass="tbx-normal"
                         Width="200px"></asp:TextBox>
                 </div>
                 <div style="text-align:center;width:100%;">
                     <asp:Button runat="server" ID="btnSalvaTFC" Text="Salvar"
                         OnClick="btnSalvaTFC_Click" CssClass="botoes" />
                     <asp:Button runat="server" ID="Button1" Text="Fechar" OnClientClick="$dvDetTpFormaContato.jqmHide(); return false;" CssClass="botoes" />
                 </div>
             </ContentTemplate>
         </asp:UpdatePanel>
     </div>
  
     <script type="text/javascript">
  
             //Inicializa os Modais
            var $dvDetContato = $('#dvDetContato').jqm({modal:true,toTop:true,trigger:false});
            var $dvDetEndereco = $('#dvDetEndereco').jqm({modal:true,toTop:true,trigger:false});
            var $dvDetFormaContato = $('#dvDetFormaContato').jqm({modal:true,toTop:true,trigger:false});
            var $dvDetTpFormaContato = $('#dvDetTpFormaContato').jqm({modal:true,toTop:true,trigger:false});
     
</script>
  
</asp:Content>

Ele pode assustar por ser gigante, mas lembre-se: a aplicação toda está contida apenas nesta página.

Temos no final um bloco de JavaScript que faz a inicialização das janelas modais do nosso sistema, usando o jQuery.

A página é composta de cinco (5) div’s principais, que representam o cadastro. O primeiro, sem um ID, é o div que corresponde à grade de pesquisa e é a primeira coisa que é mostrada para o usuário quando o site é acessado.

Nesta página, fizemos o mesmo esquema que foi feito no Cadastro de Usuários em ASP.NET (lembra?), ou seja, a página principal corresponde a 2 cadastros: Cadastro de Contatos e Tipos de Forma de Contato, sendo alternados via combo-box.

As telas de visualização, inserção e alteração de dados são janelas modais, sendo o Cadastro de Contatos e o de Tipo de Forma de Contato pertencentes à página principal: são superpostas diretamente a esta.

Já o Cadastro de Endereços e Formas de Contato, pertencem ao Cadastro de Contatos, ou seja, cada endereço e forma de contato pertencem a um contato. Portanto, estas janelas são superpostas ao Cadastro de Contato.

Por causa disto, temos a maior particularidade do design desta página em relação ao Controle de Usuários: janela modal sobre outra janela modal.

Note que nos div’s dvDetEndereco e dvDetFormaContato temos explicitamente declarado o atributo z-index com o valor 3001.

Os div’s contendo o valor jqmWindow no atributo class são janelas modais. Esta classe CSS é utilizada pelo jqModal exatamente para isso: definir os atributos das janelas modais.

O jqmWindow é definido dentro do arquivo jqModal.css, e nele, o atributo z-index padrão de cada modal é 3000.

Para obtermos a superposição de janelas modais, devemos declarar esta janela com um z-index MAIOR do que a modal que estará atrás dela. Como não dá para criar uma classe CSS para cada nível de modal, devemos sobreescrever o atributo z-index colocando-o diretamente na declaração do div a ser colocado em uma camada acima de outra.

Fazendo isso, o jqModal irá atribuir o z-index que declaramos explicitamente ao invés do z-index configurado na classe jqmWindow.

Hum… notaram mais alguma coisa de diferente aí? “O que é que você está passando no CommandArgument de cada botão Novo e nos Editar e Excluir dos grids? Antes você só passava a chave do registro…”.

Vamos ver isso analisando o code-behind:

 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 System.Runtime.Remoting;
 using System.Reflection;
 using SPIMCore;
  
 public partial class index : System.Web.UI.Page
 {
     protected void Page_Load(object sender, EventArgs e)
     {
         if (!IsPostBack)
         {
             ddlTFormaContatoTpFormaContatoId.DataSource = TTpFormaContato.ListarTodos();
             ddlTFormaContatoTpFormaContatoId.DataBind();
         }
     }
  
     protected void ddlEscolheMod_SelectedIndexChange(object sender, EventArgs e)
     {
         pnlCadContato.Visible = (sender as DropDownList).SelectedValue.Equals("1");
         pnlCadFC.Visible = (sender as DropDownList).SelectedValue.Equals("2");
     }
  
     protected void RefreshGrids()
     {
         grdContatos.DataSource = TContato.ListarTodos();
         grdContatos.DataBind();
  
         if (!tbxTContatoId.Text.Equals(""))
         {
             grdTContatoListaEnderecos.DataSource = TEndereco.ListarPorContato(Int32.Parse(tbxTContatoId.Text));
             grdTContatoListaEnderecos.DataBind();
  
             grdTContatoListaFormaContato.DataSource = TFormaContato.ListarPorContato(Int32.Parse(tbxTContatoId.Text));
             grdTContatoListaFormaContato.DataBind();
         }
  
         grdTipoFC.DataSource = TTpFormaContato.ListarTodos();
         grdTipoFC.DataBind();
     }
  
     #region Rotnias Comuns a todos os botões dos Modais
     /// <summary>
     /// Botão Editar dos gridviews
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     protected void btnEditar_Click(object sender, EventArgs e)
     {
         string[] args = (sender as Button).CommandArgument.Split(new char[1] { ',' });
         ObjectHandle clsCad = Activator.CreateInstance("SPIMCore", String.Format("SPIMCore.{0}", args[0]));
         Type t = clsCad.Unwrap().GetType();
         t.InvokeMember("SetByID", BindingFlags.InvokeMethod, null, clsCad.Unwrap(), new object[] { new object[1] { Int32.Parse(args[1]) } });
         t.InvokeMember("BindToUI", BindingFlags.InvokeMethod, null, clsCad.Unwrap(), new object[] { this, this.GetType(), new ArrayList() });
         string script = String.Format("${0}.jqmShow();", args[2]);
         ScriptManager.RegisterStartupScript(this, this.GetType(), "abremodal", script, true);
     }
  
     /// <summary>
     /// Botão Novo
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     protected void btnNovo_Click(object sender, EventArgs e)
     {
         //Não permite o Bind de combo-boxes (PK's)
         ArrayList notbind = new ArrayList();
         notbind.Add("ddlTFormaContatoTpFormaContatoId");
         
         
         string[] args = (sender as Button).CommandArgument.Split(new char[1] { ',' });
         ObjectHandle clsCad = Activator.CreateInstance("SPIMCore", String.Format("SPIMCore.{0}", args[0]));
         Type t = clsCad.Unwrap().GetType();
         t.InvokeMember("BindToUI", BindingFlags.InvokeMethod, null, clsCad.Unwrap(), new object[] { this, this.GetType(), notbind });
         string script = String.Format("${0}.jqmShow();", args[2]);
         ScriptManager.RegisterStartupScript(this, this.GetType(), "abremodal", script, true);    
     }
  
     /// <summary>
     /// Botão de Exclusão dos gridviews
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     protected void btnExcluir_Click(object sender, EventArgs e)
     {
         string[] args = (sender as Button).CommandArgument.Split(new char[1] { ',' });
         ObjectHandle clsCad = Activator.CreateInstance("SPIMCore", String.Format("SPIMCore.{0}", args[0]));
         Type t = clsCad.Unwrap().GetType();
         t.InvokeMember("SetByID", BindingFlags.InvokeMethod, null, clsCad.Unwrap(), new object[] { new object[1] { Int32.Parse(args[1]) } });
         t.InvokeMember("Delete", BindingFlags.InvokeMethod, null, clsCad.Unwrap(), null);
  
         PropertyInfo pi = t.GetProperty("MsgInfo");
  
         string script = Consts.JavaScript.Alert(Consts.Funcoes.Replacer4js(pi.GetValue(clsCad.Unwrap(), null).ToString()), false);
         ScriptManager.RegisterStartupScript(this, this.GetType(), "abremodal", script, true);
  
         RefreshGrids();
     }
  
     #endregion
  
     #region Detalhes do Contato
     protected void btnSalvarDetContato_Click(object sender, EventArgs e)
     {
         TContato contato = new TContato();
         string script = "";
         
         contato.BindFromUI(this, this.GetType(), new ArrayList());
         bool ok = (tbxTContatoId.Text.Equals("") || tbxTContatoId.Text.Equals("0")) ? contato.Insert() : contato.Update();
         
         script += Consts.JavaScript.Alert(Consts.Funcoes.Replacer4js(contato.MsgInfo), false);
         script += ok ? "$dvDetContato.jqmHide();" : "";
         if (ok)
         {
             grdContatos.DataSource = TContato.ListarTodos();
             grdContatos.DataBind();
         }
         
         ScriptManager.RegisterStartupScript(this, this.GetType(), "salvacontato", script, true);
     }
  
     protected void btnFiltrarContato_Click(object sender, EventArgs e)
     {
         grdContatos.DataSource = TContato.ListarTodos();
         grdContatos.DataBind();
     }    
     #endregion
  
     #region Modal de Endereços
     protected void btnSalvaEndereco_Click(object sender, EventArgs e)
     {
         TEndereco endereco = new TEndereco();
         string script = "";
  
         endereco.BindFromUI(this, this.GetType(), new ArrayList());
         endereco.ContatoId = Int32.Parse(hdfTContatoId.Value); //o único que deve ser setado manualmente
         bool ok = (tbxTEnderecoId.Text.Equals("") || tbxTEnderecoId.Text.Equals("0")) ? endereco.Insert() : endereco.Update();
  
         script += Consts.JavaScript.Alert(Consts.Funcoes.Replacer4js(endereco.MsgInfo), false);
         script += ok ? "$dvDetEndereco.jqmHide();" : "";
         if (ok)
         {
             grdTContatoListaEnderecos.DataSource = TEndereco.ListarPorContato(Int32.Parse(tbxTContatoId.Text));
             grdTContatoListaEnderecos.DataBind();
         }
  
         ScriptManager.RegisterStartupScript(this, this.GetType(), "salvacontato", script, true);
     }
     #endregion
  
     #region Modal de Formas de Contato
  
     protected void btnSalvaFC_Click(object sender, EventArgs e)
     {
         TFormaContato fc = new TFormaContato();
         string script = "";
  
         fc.BindFromUI(this, this.GetType(), new ArrayList());
         fc.ContatoId = Int32.Parse(tbxTContatoId.Text); //o único que deve ser setado manualmente
         bool ok = (tbxTFormaContatoId.Text.Equals("") || tbxTFormaContatoId.Text.Equals("0")) ? fc.Insert() : fc.Update();
  
         script += Consts.JavaScript.Alert(Consts.Funcoes.Replacer4js(fc.MsgInfo), false);
         script += ok ? "$dvDetFormaContato.jqmHide();" : "";
         if (ok)
         {
             grdTContatoListaFormaContato.DataSource = TFormaContato.ListarPorContato(Int32.Parse(tbxTContatoId.Text));
             grdTContatoListaFormaContato.DataBind();
         }
  
         ScriptManager.RegisterStartupScript(this, this.GetType(), "salvafc", script, true);
     }
  
     #endregion
  
     #region Modal de Tipos de Formas de Contato
     protected void btnFiltrarTFC_Click(object sender, EventArgs e)
     {
         grdTipoFC.DataSource = TTpFormaContato.ListarTodos();
         grdTipoFC.DataBind();
     }
  
     protected void btnSalvaTFC_Click(object sender, EventArgs e)
     {
         TTpFormaContato tfc = new TTpFormaContato();
         string script = "";
  
         tfc.BindFromUI(this, this.GetType(), new ArrayList());
         bool ok = (tbxTTpFormaContatoId.Text.Equals("") || tbxTTpFormaContatoId.Text.Equals("0")) ? tfc.Insert() : tfc.Update();
  
         script += Consts.JavaScript.Alert(Consts.Funcoes.Replacer4js(tfc.MsgInfo), false);
         script += ok ? "$dvDetTpFormaContato.jqmHide();" : "";
         if (ok)
         {
             grdTipoFC.DataSource = TTpFormaContato.ListarTodos();
             grdTipoFC.DataBind();
             ddlTFormaContatoTpFormaContatoId.DataSource = TTpFormaContato.ListarTodos();
             ddlTFormaContatoTpFormaContatoId.DataBind();
         }
  
         ScriptManager.RegisterStartupScript(this, this.GetType(), "salvatfc", script, true);
     }
  
     #endregion
 }

Vamos direto no que você reparou de diferente ;-)

No nosso código, temos um region chamado “Rotinas comuns a todos os botões de modais”. É isso mesmo! o código contido nela é comum a todos os modais! E olha só, são exatamente os botões Novo, Editar e Excluir de cada cadastro :-)

No CommandArgument da cada botão, eu passo o seguinte, através do #Bind: Nome da Classe, Chave do Registro (que é passado no primeiro parâmetro do #Bind) e o Nome do Modal que esta janela abre. Isto é feito formatando-se o resultado do comando #Bind, sendo que onde está o “{0}” será substituído pelo valor do primeiro parâmetro do comando #Bind, no nosso caso, o campo Id da tabela.

Notaram que todos estes botões apontam para UMA única rotina em seu evento OnClick (uma para cada ação: novo, editar e excluir)? Porém eles manipulam classes diferentes!

Pois é, classes diferentes, porém concebidas da mesma maneira!

Estudando o evento OnClick do botão Editar, vemos que ele separa cada argumento (que separei por vírgula) em um array de string, a nossa variável args.

Temos depois disso a primeira novidade: instanciamos um objeto em tempo de execução!

Para isso, faremos uso do namespace System.Runtime.Remoting e das classes ObjectHandle e Activator.

A primeira é uma classe que armazena referências de objetos, e a segunda cria-os, basicamente.

Em uma instância de ObjectHandle (variável clsCad), criamos em tempo de execução uma instância da classe que passamos no CommandArgument do botão Novo. Para isso, utilizamos o método estático CreateInstance da classe Activator, que na sobrecarga que utilizei pede como parâmetros o nome do assembly onde esta classe se encontra e o nome completo da classe (o namespace e o nome da classe) e retorna uma instância de ObjectHandle, que contém a referência deste objeto que criamos. No índice zero da variável args, gravamos o nome da classe que pegamos no CommandArgument e utilizamos neste método.

Em seguida, pegamos as informações de tipo da classe criada em uma instância de uma classe Type. Note que não usei o método GetType() da classe clsCad diretamente: Se eu fizesse isso, o método GetType() iria me retornar as informações da classe ObjectHandle, e não da classe de cadastro que instanciei, e que está encapsulada neste objeto.

Para pegar as informações do objeto encapsulado em si, chamo o método Unwrap() do objeto clsCad, que aí sim, temos o nosso objeto de cadastro à disposição, e a partir dele chamo o GetType().

Com isso, chamaremos os métodos de exibição e atribução dos valores na UI através do método InvokeMember do nosso objeto Type.

O método InvokeMember pede como parâmetros o nome do método em questão, os BindingFlags, que no nosso caso utilizamos BindingFlags.InvokeMethod, o Binder padrão, que no nosso caso utilizamos null, o objeto em que o método será chamado, onde utilizamos a nossa variável clsCad com o método Unwrap(), e um array de object contendo os parâmetros para o método a ser chamado.

No método SetByID(), passamos como parâmetro um array de object contendo as chaves primárias do registro a ser mostrado. Ele é obtido através do índice 1 da variável args.

O método BindToUI() pede como parâmetros a página em que estão os controles a ser atribuídos, as informações de seu tipo e um arraylist indicando quais controles não serão atribuídos.

Para usar o BindToUI, necessitamos nomear cada componente seguindo a convenção: Prefixo de 3 letras, Nome da Classe e Nome da Propriedade.

Feito tudo isso, registramos um JavaScript com o comando para abrir a janela modal referente ao cadastro; janela esta que é passado no terceiro item da variável args.

Notem que TODAS as classes trabalham da mesma forma. Esta maneira de trabalhar e as convenções adotadas foi o que permitiu que escrevessemos um só trecho de código para quatro classes distintas.

Os botões Novo e Excluir trabalham da mesma maneira. A difereça entre eles é:

No botão Novo, criamos um ArrayList para não atribuirmos valor no combo-box de tipos de forma de contato do Cadastro de Formas de Contato de cada contato, pois como é inicializado um objeto SEM DADOS, o valor inicializado na propriedade correspondente possui algo que não existe no combo-box, e isso ocasiona um erro de execução.

No botão Excluir, após executarmos o método Delete() (dinamicamente!), pegamos o valor da propriedade MsgInfo através de um objeto PropertyInfo (que vimos anteriormente) e disparamos para o usuário.

Nos botões de Salvar temos as particularidades de cada classe, como o “refresh” dos grids e alguns campos de chave estrangeira que são atribuídos manualmente, sendo os outros atribuídos via método BindFromUI.

E por fim, terminamos a interface Web e fechamos a exemplificação dos métodos novos mais importantes da nova Classe de Conexão, e no próximo post tecerei algumas conclusões sobre o assunto.

Projeto de Exemplo da Classe de Conexão – Simple PIM (263 kB)

Um abraço!