Criando Controles de Servidor Personalizados com Validação
Neste artigo vamos utilizar as práticas descritas no artigo "Iniciando Expressões Regulares" para criação de controles de servidor personalizados com validação.
Uma boa prática no desenvolvimento de sistemas é o tratamento de erros. Você já deve ter desenvolvido uma página com um controle TextBox, por exemplo, que inclusive estava com uma boa formatação e podia receber somente números, e derrepente quando uma outra pessoa - usuário final - foi utilizar gerou uma mensagem de erro? Você pode até arrastar um controle de validação para o formulário... uma vez... duas vezes... e mais... e mais... até você querer abreviar este trabalho criando um controle personalizado que possua controles de validação para tipos predefinidos, mensagens de erro personalizadas e outras coisas mais..
Um controle de servidor do ASP.NET renderiza (constroe) marcações, assim como script do lado cliente.
Controles personalizados de servidor podem ser criados a partir da herança de um controle existente ou herdado diretamentamente da classe base.
Devemos estar atentos quanto a reutilização do controle que iremos criar. Se deseja usar seu controle personalizado em múltiplos Web sites então desenvolva sua classe em um projeto do tipo Class Library para gerar uma .dll, que neste caso poderá ser compartilhada. Se desejar usar seu controle somente em sua página corrente da Web então adicione sua classe em seu Web site.
Podemos facilmente criar uma herança de um controle existente e adicionarmos mais propriedades, métodos e eventos. Pode-se ainda, sobreescrever métodos e propriedades para apresentar diferentes comportamentos.
Vamos praticar...
- Crie um projeto do tipo Class Library (sugestão nome CustomControls);
- Adicione uma class ao seu projeto ou utilize a classe gerada dinamicamente (sugestão nome CustomTextBox);
- Importe os seguintes namespaces: System.Web.UI; System.Web.UI.WebControls; System.Text.RegularExpressions;
- Sua classe deve herdar a classe Control e INamingContainer;
- Crie no mesmo namespace um enumerador (enum) com os tipos que serão predefinidos para validação (sugestão nome TypeValidation);
- Propriedade ValueType tipo TypeValidation (tipo predefinido);
- Crie um objeto tipo RegularExpressionValidator com acesso as propriedades: ValidationExpression tipo String (expressão de validação), ErrorMessage tipo String (mensagem de erro), Display tipo ValidatorDisplay (manipula apresentação do validator) ...;
- Crie um objeto tipo TextBox com acesso as propriedades: Text tipo String (Text do controle), override ID tipo String (ID do controle), Width tipo Unit (largura do controle);
- Crie uma propriedade do tipo booleana para verificar o preenchimento obrigatorio (sugestão nome IsNotNull);
- Sobreescreva o método CreateChildControls com chamada a três métodos que você irá implementar, a saber: CreateTextBox (manipular o controle TextBox), CreateRegularExpressionValidator (manipular o controle RegularExpressionValidator - faz validação baseado em expressões regulares), CreateRequiredFieldValidator (manipular o controle RequiredFieldValidator - resposável por verificar os campos obrigatórios, ou melhor, que obrigatoriamente devem estar preenchidos) e CreateSummary (manipular o controle SummaryValidator - resposável pela apresentação de mensagens);
Recursos Principais | |
Control | Classe base que compartilha propriedades, métodos e eventos para todos os controles de servidor no ASP.NET. A classe Control descreve a classe base para controles, que são componentes com representação visual. Control é a primeira classe que você deriva quando está desenvolvendo controles personalizados no ASP.NET. |
INamingContainer | Interface que identifica um controle container que cria um novo namespace em um objeto Page hierarquicamente. |
CreateChildControls | Cria controles filhos. |
O objetivo deste componente é trabalhar com expressões regulares predefinidas ou não (RegularExpressionValidator) e validação de campos obrigatorios (RequiredFieldValidator), lembrando que não será necessário nenhum esforço em relação a arrastar o ValidationSummary para sua página para apresentar as mensagens..., pois o código também faz isso dinamicamente. Confira o código abaixo:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
namespace CustomControls
{
/// <summary>
/// Tipos
Predefinidos
/// </summary>
public enum
TypeExpression : short
{
None,
Digito, Decimal, Moeda, Email, Telefone
}
/// <summary>
/// Classe que apresenta controle TextBox com controle de
validação
/// </summary>
public class
CustomTextBox : Control,
INamingContainer
{
private TypeExpression
_valueType;
/// <summary>
/// Tipos predefinidos
/// </summary>
public TypeExpression
ValueType
{
get { return
_valueType; }
set { _valueType = value; }
}
/// <summary>
/// Controle de
Validação
/// </summary>
RegularExpressionValidator _validator = new RegularExpressionValidator();
/// <summary>
/// Expressão de
Validação
/// </summary>
public String
ValidationExpression
{
get { return
_validator.ValidationExpression; }
set { _validator.ValidationExpression = value; }
}
/// <summary>
/// Mensagem de Erro
/// </summary>
public String
ErrorMessage
{
get { return
_validator.ErrorMessage; }
set { _validator.ErrorMessage = value; }
}
/// <summary>
/// Manipula apresentação do validator
/// </summary>
public ValidatorDisplay Display
{
get { return
_validator.Display; }
set { _validator.Display = value; }
}
/// <summary>
/// Controle TextBox
/// </summary>
TextBox _textBox = new
TextBox();
/// <summary>
/// Propriedade Text do
controle
/// </summary>
public String Text
{
get { return
_textBox.Text; }
set { _textBox.Text = value; }
}
/// <summary>
/// Propriedade ID do
controle
/// </summary>
public override String ID
{
get { return
_textBox.ID; }
set { _textBox.ID = value; }
}
/// <summary>
/// Largura do Controle
/// </summary>
public Unit Width
{
get { return
_textBox.Width; }
set { _textBox.Width = value; }
}
private Boolean
_isNull;
/// <summary>
/// Preenchimento
obrigatorio
/// </summary>
public Boolean
IsNotNull
{
get { return
_isNull; }
set { _isNull = value;
}
}
/// <summary>
/// Cria controles filhos
/// </summary>
protected override void CreateChildControls()
{
// manipula TextBox
CreateTextBox();
// manipula RegularExpressionValidator
CreateRegularExpressionValidator();
// manipula
RequiredFieldValidator
CreateRequiredFieldValidator();
// manipula SummaryValidator
CreateSummary();
}
/// <summary>
/// Monta TextBox e adiciona controle
/// </summary>
private void
CreateTextBox()
{
// mude a aparência do controle, etc.
aqui
// ex.: _textBox.CssClass =
"meuCss";
// adiciona controle TextBox
Controls.Add(_textBox);
}
/// <summary>
/// Monta expressão
com o tipo predefinido e adiciona (se expressão informada)
/// </summary>
private void
CreateRegularExpressionValidator()
{
// fixa o controle que será validado
_validator.ControlToValidate = _textBox.ID;
// monta expressão
if (String.IsNullOrEmpty(this.ValidationExpression))
{
// verifica tipo predefinido
switch (this.ValueType)
{
case TypeExpression.Digito:
if (string.IsNullOrEmpty(this.ErrorMessage))
this.ErrorMessage = "Somente dígitos.";
this.ValidationExpression = @"^\d+$";
break;
case TypeExpression.Decimal:
if (string.IsNullOrEmpty(this.ErrorMessage))
this.ErrorMessage = "Formato decimal inválido.";
this.ValidationExpression = @"^[+-]?((\d+|\d{1,3}(\.\d{3})+)(\,\d*)?|\,\d+)$";
break;
case TypeExpression.Moeda:
if (string.IsNullOrEmpty(this.ErrorMessage))
this.ErrorMessage = "Formato moeda inválido.";
this.ValidationExpression = @"^\d{1,3}(\.\d{3})*\,\d{2}$";
break;
case TypeExpression.Email:
if (string.IsNullOrEmpty(this.ErrorMessage))
this.ErrorMessage = "Formato e-mail inválido.";
this.ValidationExpression = @"^([\w\-]+\.)*[\w\-
]+@([\w\- ]+\.)+([\w\-]{2,3})$";
break;
case TypeExpression.Telefone:
if (string.IsNullOrEmpty(this.ErrorMessage))
this.ErrorMessage = "Formato telefone inválido.";
this.ValidationExpression = @"^\(\d{3}\)-\d{4}-\d{4}$";
break;
}
}
// adiciona controle (se conter expressão)
if(!String.IsNullOrEmpty(this.ValidationExpression))
Controls.Add(_validator);
}
/// <summary>
/// Monta
RequiredFieldValidator e adiciona (se campo obrigatorio)
/// </summary>
private void
CreateRequiredFieldValidator()
{
// se campo obrigatorio
if (this.IsNotNull)
{
// cria RequiredFieldValidator e fixa propriedades
RequiredFieldValidator _required = new RequiredFieldValidator();
_required.ControlToValidate = this.ID;
// ** sugestão: crie uma propriedade
para esta
// mensagem de erro
_required.ErrorMessage = String.Empty;
_required.Text = "* obrigatorio";
_required.Display = ValidatorDisplay.Dynamic;
_required.ForeColor = System.Drawing.Color.Red;
// adiciona controle
Controls.Add(_required);
}
}
/// <summary>
/// Monta ValidationSummary
e adiciona (se possuir validação)
/// </summary>
private void CreateSummary()
{
// verifica as propriedades obrigatorias
if (!String.IsNullOrEmpty(this.ValidationExpression) || this.IsNotNull)
{
// contador de verificação dos
controles CustomTextBox
Int16 count = 0;
// percorre todos os controles da
página
foreach (Control _control in Page.Form.Controls)
{
// se controle for do tipo da classe
// personalizada que estamos desenvolvendo
if (_control.GetType() == typeof(CustomTextBox))
{
// se o objeto está sendo acessado pela
// primeira vez
if (count == 0)
// se o controle encontrado é o
controle que
// provocou a chamada
if (_control.ID == this.ID)
{
// cria Summary e fixa propriedades
ValidationSummary _summary = new ValidationSummary();
// ** sugestão: preencha esta
propriedade
// para mensagem comuns
//_summary.HeaderText = "Verifique os
// campos Obrigatórios.";
_summary.ShowMessageBox = true;
_summary.ShowSummary = false;
_summary.ID = "vsTextBox";
_summary.DisplayMode = ValidationSummaryDisplayMode.List;
// adiciona controles
Controls.Add(_summary);
}
// incrementa à quantidade de controles
do
// tipo CustomTextBox encontrados
count++;
}
}
}
}
}
}
É aconselhável que você adquira a prática de fazer comentários interno e externo, pois facilita na manutenção. Reaproveitamento de código também é bastante relevante, por exemplo, os pontos semelhantes de execução é interessante unir em um só método visando reutilização de código - citamos o código acima que ao invés de criarmos métodos manipulando os tipos predefinidos, implementamos um enumerador bem simples.
Desenvolva o componente sempre pensando em padronização. Por exemplo, verifique se a mensagem de erro do controle de validação será estática, dinâmica ou sem visualização, aparência do controle, etc. e implemente com valores fixos no método CreateChildControls.
Lembre-se que as únicas propriedades que aparecerão serão as da classe Control juntamente com as que foram criadas, por exemplo, a propriedade Row ou TextMode que é específica do controle TextBox não aparecerão, a não ser que você crie essa propriedade e manipule conforme fizemos, por exemplo, com a propriedade ValidationExpression, que é específica do controle RegularExpressionValidator. Dessa forma, sugiro implementar as propriedades MaxLength (TextBox), ErrorMessage (RequiredFieldValidator) e etc...
Adicione a referência do seu componente no projeto Web. Arraste o controle CustomTextBox para sua página ou então faça:
< %@ Page Language="VB"
AutoEventWireup="false"
CodeFile="Validation.aspx.vb"
Inherits="Default5"
%>
< %@ Register Assembly="CustomControls"
Namespace="CustomControls"
TagPrefix="cc1"
%>
< !DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns="http://www.w3.org/1999/xhtml"
>
< head runat="server" >
< title>Untitled Page< /title>
< /head>
< body>
< form
id="form1"
runat="server" >
< div>
< !-- campo obrigatorio -->
< cc1:CustomTextBox ID="CustomTextBox1"
runat="server"
IsNotNull="true"
/>< br />
< !-- campo obrigatorio e somente dígito -->
< cc1:CustomTextBox ID="CustomTextBox2"
runat="server"
ValueType="Digito"
Display="None"
IsNotNull="true"
/>< br />
< !-- campo não obrigatorio com validaç ão de e-mail
e mensagem de erro externo -->
< cc1:CustomTextBox ID="CustomTextBox3"
runat="server"
ValueType="Email"
Display="Dynamic"
ErrorMessage="E-mail
inválido" />< br />
< !-- campo não obrigatorio com validaç ão de
telefone e mensagem de erro interna -->
< cc1:CustomTextBox ID="CustomTextBox4"
runat="server"
ValueType="Telefone"
Display="Static"
/>< br />
< asp:Button ID="Button1"
runat="server"
Text="Button"
/>
< /div>
< /form>
< /body>
< /html>
O mérito da criatividade é seu. Você pode criar várias outras coisas utilizando este simples exemplo. Tente desenvolver este mesmo componente utilizando os tipos predefinidos do controle RangeValidator.
Referências:
MCTS Self-Paced Training Kit (Exam 70-536): Microsoft .NET Framework 2.0 Application Development Foundation / byTony Northrup, Shawn WildermuthandBill Ryan;
http://msdn2.microsoft.com/en-us/library/system.web.ui.control.aspx;
http://msdn2.microsoft.com/en-us/library/system.web.ui.control.createchildcontrols(VS.71).aspx;
http://msdn2.microsoft.com/en-us/library/system.web.ui.control.onprerender(VS.71).aspx;
http://msdn2.microsoft.com/en-us/library/system.web.ui.ivalidator_members.aspx;