Criação de controles dinâmicos em .NET

Posted by Leticia Verta | Posted in .NET, C#, Geral | Posted on 18-12-2009-05-2008

2

Eis uma coisa que sempre encafifava minhas idéias: criação de controles dinâmicos no .NET.
Parece simples, porém, devido ao ‘complexo’ ciclo de vida das páginas da linguagem, se torna um pouco mais chatinho.
Vamos para o exemplo … que eu usei ainda hoje, diga-se de passagem :P

Criaremos uma página com um Panel e um Button

<!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>Relaxos - Controles Dinâmicos</title>
  </head>
  <body>
    <form id="form1" runat="server">
    <div>
      <asp:Panel ID="pnlTeste" runat="server" />
      <asp:Button ID="btnExibir" runat="server" Text="Botãozar" onclick="btnExibir_Click" />
    </div>
    </form>
  </body>
</html>

Agora criaremos, em tempo de execução, um TextBox e um Button – e seu respectivo evento. Exibiremos o conteúdo dos controles já adicionados através do btnExibir (notem que eu já setei um evento ao botão).

private TextBox txtDinamico;
private Button btnDinamico;
 
protected void Page_Load(object sender, EventArgs e)
{
  CriarControles();
}
 
private void CriarControles()
{
  txtDinamico = new TextBox();
  pnlTeste.Controls.Add(txtDinamico);
 
  btnDinamico = new Button();
  btnDinamico.Text = "+";
  btnDinamico.Click += new EventHandler(Adicionar);
  pnlTeste.Controls.Add(btnDinamico);
}
 
void Adicionar(object sender, EventArgs e)
{
  CriarControles();
}
 
protected void btnExibir_Click(object sender, EventArgs e)
{
  StringBuilder stbConteudo = new StringBuilder();
 
  //Pegamos todos os controles do pnlTeste
  foreach (Control oControl in pnlTeste.Controls)
  {
    //E filtramos para pegar os valores apenas dos TextBox's
    if (oControl.GetType().Name == "TextBox")
      stbConteudo.Append(String.Concat("\\nControle -> ", ((TextBox)oControl).UniqueID, " - ", ((TextBox)oControl).Text));
  }
 
  Response.Write(String.Concat("alert('" , stbConteudo.ToString(), "');"));
}

Rodando (ma oe) o projeto agora, vocês verão que os controles se perdem forever. A cada PostBack os controles somem, é como se nunca tivesse sido incluso nada ali. Isso acontece porque apenas as propriedades desses controles são serializadas, o controle em si não é, ou seja, teremos que persistir esses controles na raça … então let’s go:

public partial class ControlesDinamicos : System.Web.UI.Page
{
    private TextBox txtDinamico;
    private Button btnDinamico;
    private int qtde 
    {
        get { return Session["qtde"] == null ? 0 : Convert.ToInt32(Session["qtde"]); }
        set { Session["qtde"] = value; }
    }
    private int indice = 0;
 
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            //Cria o primeiro controle
            CriarControles(false);
        else
        {
            //Mantém controles posteriormente criados
            for (int c = 0; c < qtde; c++)
                CriarControles(true);
        }
    }
 
    private void CriarControles(bool Persistencia)
    {
        txtDinamico = new TextBox();
        txtDinamico.ID = String.Concat("txtDinamico", indice);
        pnlTeste.Controls.Add(txtDinamico);
 
        btnDinamico = new Button();
        btnDinamico.Text = "+";
        btnDinamico.ID = String.Concat("btnDinamico", indice);
        btnDinamico.Click += new EventHandler(Adicionar);
        pnlTeste.Controls.Add(btnDinamico);
 
        indice++;
 
        if (!Persistencia)
            qtde++;
    }
 
    void Adicionar(object sender, EventArgs e)
    {
        CriarControles(false);
    }
 
    protected void btnExibir_Click(object sender, EventArgs e)
    {
        StringBuilder stbConteudo = new StringBuilder();
 
        //Pegamos todos os controles do pnlTeste
        foreach (Control oControl in pnlTeste.Controls)
        {
            //E filtramos para pegar os valores apenas dos TextBox's
            if (oControl.GetType().Name == "TextBox")
                stbConteudo.Append(String.Concat("\\nControle -> ", ((TextBox)oControl).UniqueID, " - ", ((TextBox)oControl).Text));
        }
 
        Response.Write(String.Concat("<script>alert('" , stbConteudo.ToString(), "');</script>"));
    }
}

No código acima, quando acontece a persistência (que nada mais é a recriação de novos controles), os controles mudam os ids (Ex.: existem os controles ctl01, ctl02 … PostBack … na recriação serão ctl03, ctl04, por ai vai) e mais uma vez, cadê os dados?? Pra evitar essa bagunça, setamos a identidade de cada controle e VUALÁ! :)

Bom, agora é só colocar um ajaxzinho pra ficar biito e adequar às necessidades diárias … quem quiser o código, só clicar.

[]’s

Leticia Verta, desenvolvedora aspirante em .NET rs

TDD – Test-driven Development + c# – Parte II

Posted by admin | Posted in .NET, C#, TDD | Posted on 06-11-2009-05-2008

0

Original em Vinicius Quaiato: http://viniciusquaiato.com/blog/index.php/tdd-test-drive…ent-c-parte-ii/

TDD – Test-driven Development – Parte I
TDD – Test-driven Development – Parte III

Após o que foi apresentado no primeiro post da série, faremos nesta segunda parte um exemplo prático de TDD.

Para efeito didático este será um exemplo bem simples e trivial, para que possamos focar nas práticas de TDD.

Antes de começarmos gostaria de mencionar o “mantra do TDD”:

Red, green, refactoring

Ou seja, escreva um teste que falhe, na verdade um teste que nem compila (vermelho). Escreva um código que compile e faça o teste passar (verde), mesmo que seja um código ruim. Então, após o teste passar melhoramos o código, com a finalidade de deixá-lo mais claro, coeso e simples (refactoring).

Tendo isso em mente, vamos para o nosso caso de uso em C#:

Desenvolver uma aplicação bancária que controle saques, depósitos e transferências.

Como eu disse o caso de uso é simples, para que foquemos no TDD.

Vamos criar uma nova solution no Visual Studio. Aqui já começa a mudança de pensamento. Antes de criar o projeto da conta bancária, eu começo criando o projeto de testes da conta bancária. Eu gosto de pensar assim para ir me acostumando com a idéia do Test First.

TDD_criando_projeto_testes

TDD - criando projeto de testes

Agora que estamos com o projeto criado, vamos escrever nosso primeiro teste. Vocês vão perceber que a prática do TDD nos leva a pensar melhor em nossas classes. Vamos ver.

Bom, em nosso primeiro teste vamos nos assegurar de que quando uma conta seja criada ela obrigatóriamente necessite de um depósito inicial:

[TestMethod]
public void Deve_Criar_Conta_Com_Deposito_Inicial()
{
    ContaBancaria conta = new ContaBancaria(50.0m);
 
    Assert.AreEqual(50, conta.SaldoAtual);
}

Como podemos ver, este teste nem irá compilar, afinal, estamos criando o teste antes mesmo de criarmos a classe ContaBancaria.

public class ContaBancaria
{
    public decimal SaldoAtual { get; set; }
    public ContaBancaria(decimal depositoInicial) { }
}

Assi podemos executar nosso teste. Eu gosto de utilizar dois atalhos CTRL + R + T (executa o teste corrente em modo debug) ou CTRL + R + A (executa todos os testes da solution em modo debug).
Teremos o resultado como abaixo:

TDD - executando primeiro teste

TDD - executando primeiro teste

Este teste falhou. Ótimo! Obtemos um red e sabemos que estamos no caminho certo. Aconteceu que estávamos esperando um saldo de 50 e o saldo obtido foi 0.
Agora devemos voltar ao código e fazer o teste passar:

public class ContaBancaria
{
    public decimal SaldoAtual { get; set; }
    public ContaBancaria(decimal depositoInicial)
    {
        this.SaldoAtual += depositoInicial;
    }
}

Neste caso nossa mudança é bem pequena. Agora vamos executar o mesmo teste e ver o que acontece:

TDD - executando primeiro teste verde

TDD - executando primeiro teste verde

Pronto! Obtivemos um verde, e isso quer dizer que podemos prosseguir, escrevendo os próximos testes. Antes disso acontecer, devemos lembrar do próximo passo: refactoring!
Vamos voltar ao nosso código e entender o que pode ser refatorado.
Me parece que a propriedade SaldoAtual não deveria ter um setter público, desta forma vamos torná-lo privado:

public class ContaBancaria
{
    public decimal SaldoAtual { get; private set; }
    public ContaBancaria(decimal depositoInicial)
    {
        this.SaldoAtual += depositoInicial;
    }
}

Agora devemos novamente executar nossos testes, para termos certeza de que não acabamos estragando nada.

Agora precisamos garantir que o depósito inicial seja um depósito válido. Ou seja, o que acontece com um depósito inicial igual a 0? E se for menor que 0?
De acordo com a regra de negócio que eu inventei, uma conta sempre deve ser criada com um depósito inicial. Quando quisermos carregar uma conta específica, utilizaremos outra notina e não o construtor público.
Assim, vamos criar um deste que garanta que o depósito inicial seja válido.

Vamos usar um atributo(attribute) do framework de testes do visual studio que nos permite testar se um determinado teste lança uma exceção – ExpectedException:

[TestMethod]
[ExpectedException(typeof(DepositoInicialInvalidoException))]
public void Deve_Lancar_Excecao_Deposito_Inicial_Invalido()
{
    ContaBancaria conta = new ContaBancaria(0);
}

Para que este teste compile, vamos criar uma Exception:

public class DepositoInicialInvalidoException : Exception
{
    public DepositoInicialInvalidoException()
        : base("Depósito inicial deve ser um valor maior que 0(Zero)!") { }
}

Agora vamos rodar nosso test. Pumba! Obtivemos um red. Afinal, não estamos validando o depósito inicial. Vamos fazer o código passar:

public ContaBancaria(decimal depositoInicial)
{
    if (depositoInicial &lt;= 0)
        throw new DepositoInicialInvalidoException();
 
    this.SaldoAtual += depositoInicial;
}

Agora já podemos executar nossos testes (sim, devemos executar todos os testes, para ter certeza que nada quebrou no meio do caminho). Pronto, temos um verde. Vamos para o refactoring. A validação dentro do construtor não ficou muito elegante. Afinal, no futuro podemos ter outros tipos de validações, desta forma vamos refatorar:

TDD - refatorando

TDD - refatorando

Assim o visual studio criará para nós um método, e ficaremos com a seguinte estrutura:

public ContaBancaria(decimal depositoInicial)
{
    Validar(depositoInicial);
 
    this.SaldoAtual += depositoInicial;
}
private void Validar(decimal depositoInicial)
{
    if (depositoInicial &lt;= 0)
        throw new DepositoInicialInvalidoException();
}

É importante que a cada refatoring, cada mudança de código, executemos novamente os testes. Mesmo que achemos que a mudança é pouca e pequena, isso nos fará ter o hábito de sempre executar os testes, e no futuro faremos isso para mudanças mais drásticas e severas.

Bom pessoal, nesta segunda parte espero ter mostrado o início do TDD utilizando C# + Visual Studio.

Na próxima parte deste artigo criaremos os testes para depósitos e saques.

Abraços,
Vinicius Quaiato.

.NET C# 3.5 – Features – Parte II

Posted by Vinicius Quaiato | Posted in .NET, C# | Posted on 20-03-2009-05-2008

0

Continuando nosso artigo sobre as features do c# 3.0 e .NET 3.5.

Parte I:
http://blog.relaxos.com.br/geral/net-csharp-35-features-parte-i/

Vamos continuar falando sobre Colletion Initializers.

Colletion Initializers: São parecidos com Object Initializers, porém para coleções. Esta feature permite a você definir elementos para a coleção no memento da criação, desde que a mesma implemente IEnumerable.

Vamos ver o primeiro caso, uma lista de strings:

List<string> strings = new List<string>{ "Eu" , "Programando" , ".NET C# 3.5" };

Poderíamos também ter uma lista com uma classe que criamos, e utilizar Collection e Object Initializers juntos:

public class Pessoa
{
    public string Nome{get; set;}
    public string Sobrenome{get; set;}
}

List<Pessoa> pessoas = new List<Pessoa>{
    new Pessoa(){Nome = "Vinicius",Sobrenome = "Quaiato"},
    new Pessoa(){Nome = "Programando",Sobrenome = ".NET 3.5 C#"}
};

 

Implicitly Typed Local Variables: O .NET 3.0 possui uma keyword chamada var. Ela é diferente das antigas variáveis var do VB6. Elas são locais e fortemente tipadas, apesar de ter tipo implícito.

Por ser fortemente tipada, uma var que contenha um inteiro, é do tipo inteiro, implicitamente, e não pode receber outro tipo.
Confuso? Bom, vamos aos exemplos que são mais fáceis de entender:

var inteiro = 10;
int outroInteiro = inteiro;

Isso é possível pois o compilador entende à partir do momento que a variável inteiro recebe um valor int, ela assume o tipo int, e não se pode mais modificar isso.
E para provar, vamos pegar o Type da nossa var inteiro, e do nosso int outroInteiro:

var inteiro = 10;
int outroInteiro = inteiro;

Console.WriteLine("Type of inteiro: {0}\nType of outroInteiro: {1}", inteiro.GetType().Name, outroInteiro.GetType().Name);

Teremos a seguinte saída no console:
var1

E esta verificação é feita em tempo de compilação, ou seja, escrever o seguinte código não compilaria nosso programa:

var inteiro = 10;
inteiro = "vinicius";

 

Anonymous Types: Tipos anônimos são tipos locais, têm seus nomes definidos em runtime e os tipos de suas propriedades inferidos implicitamente (assim como nos tipos var)

São uma maneira interessante de encapsular informações no escopo de um método.
Vamos entender sob a perspectiva da seguinte situação:

Temos um método onde dentro dele precisamos trabalhar com o Nome de um Cliente, o Valor total de um Pedido, uma data de Entrega e um Código de produto.

Se não quiséssemos manter todos esses objetos carregados em memória, imaginando que eles são muito custosos, poderíamos ter um tipo anônimo para guardas estas informações que de fato vamos trabalhar, exemplo:

var anonymousType = new {
                            Nome = "Vinicius Quaiato",
                            Valor = 10.75,
                            Data = DateTime.Now.AddDays(3),
                            Produto = new Guid()
                        };

Ou seja, acabamos de criar um tipo novo, que não sabemos o nome, mas definimos propriedades para ele. E logo após fazer isso, o intellisense está disponível com essas properties. vejamos:
anonymous

E para comprovar que de fato criamos um novo objeto de um novo tipo, vamos executar o seguinte código:

var anonymousType = new {
                            Nome = "Vinicius Quaiato",
                            Valor = 10.75,
                            Data = DateTime.Now.AddDays(3),
                            Produto = new Guid()
                        };

Console.WriteLine("\nType Name:\n{0}", anonymousType.GetType().Name);

Console.WriteLine("\n\nProperties of anonymousType object:\n {0}", anonymousType);

E teremos a saída:
anonymous2

Bom, é isso pessoal. Estas features do C# 3.0 e .NET 3.5 são incríveis, agora é usar a imgainação e aproveirar.

Continuaremos a terceira e última parte deste artigo falando sobre:

  • Extension methods – Métodos de extensão
  • Lambda expressions – Expressões Lambda
  • Query expressions – LINQ
    • Obrigado pelas visitas, comentem e voltem sempre!

      Vinicius Quaiato