ASP .NET - Gerador de Cartões (revisão Web Forms para novatos)


Neste artigo eu vou criar um gerador de cartões usando ASP .NET e a tecnologia Web Forms. O objetivo é rever alguns conceitos básicos para quem esta iniciando no aprendizado ASP .NET.

É importante lembrar que a plataforma .NET além da ASP .NET possui também a tecnologia ASP .NET MVC que proporciona uma nova abordagem que permite um controle maior do desenvolvedor sobre a aplicação web.

Neste exemplo vamos usar alguns controles ASP .NET Web Forms básicos e veremos como tratar os eventos e a manipular os controles com suas propriedades básicas tudo isso criando uma aplicação simples que você poderá depois incrementar se assim o desejar.

Neste artigo você vai aprender a :

Requisitos:

Objetivos

Criar um gerador de cartões com opções para o usuário definir cor de fundo, fonte, tamanho da fonte, estilo da borda, imagem e texto.

Criando o projeto no Visual Studio 2012 Express for web

Abra o Visual Studio 2012 Express for web e clique em New Project;

A seguir selecione o template Visual C# -> Web -> ASP .NET Empty Web Application, informe o nome GeradorDeCartoes e clique em OK;

O template usado cria um projeto contendo apenas a estrutura básica sem nenhuma página.

A seguir no menu PROJECT clique em Add New Item e selecione o template Web Form informando o nome Default.aspx e clicando no botão Add;

Agora no clique com o botão direito do mouse sobre o nome do projeto e selecione Add -> New Folder e informe o nome Imagens. Criamos assim uma pasta chamada Imagens para guardar as imagens usadas nos cartões.

Clique com o botão direito do mouse sobre a pasta Imagens e selecione Add-> Existing Item e selecione as imagens que serão armazenadas nesta pasta.

Vamos definir o código da página Default.aspx, para isso selecione o arquivo Default.aspx, na janela Solution Explorer, e a seguir no descritor, clique na aba Source e inclua o código abaixo neste arquivo:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GeradorDeCartoes.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Gerador de Cartões</title>
</head>
<body>
    <form id="form1" runat="server">
        <div style="BORDER-RIGHT: thin ridge; PADDING-RIGHT: 20px; BORDER-TOP: thin ridge; PADDING-LEFT: 20px;
	        FONT-SIZE: x-small; PADDING-BOTTOM: 20px; BORDER-LEFT: thin ridge; WIDTH: 295px; PADDING-TOP: 20px;
	        BORDER-BOTTOM: thin ridge; FONT-FAMILY: fantasy; HEIGHT: 490px; BACKGROUND-COLOR: lightyellow">
            <!-- Controles do formulário: -->
            Escolha uma cor de fundo:<br />
            <asp:DropDownList ID="lstCorDeFundo" runat="server" Width="194px" Height="22px"/><br /><br />
            Escolha a fonte:<br />
            <asp:DropDownList ID="lstNomeDaFonte" runat="server" Width="194px" Height="22px" /><br /><br />
            Defina o tamanho da fonte:<br />
            <asp:TextBox ID="txtTamanhoDaFonte" runat="server" >10</asp:TextBox>
            <br /><br />
            Escolha um estilo de borda:<br />
            <asp:RadioButtonList ID="lstBorda" runat="server" Width="177px" Height="59px" /><br />
            Escolha uma Imagem :<br />
            <asp:DropDownList ID="lstImagem" runat="server" Width="194px" Height="22px" OnSelectedIndexChanged="lstImagem_SelectedIndexChanged" /><br /><br />
            <asp:CheckBox ID="chkImagem" runat="server" Text="Exibir Imagem"></asp:CheckBox><br /><br />
            Digite o texto do cartão abaixo:<br />
            <asp:TextBox ID="txtTexto" runat="server" Width="240px" Height="85px" TextMode="MultiLine" >Seu Texto aqui</asp:TextBox>
            <br /><br />
            <asp:Button ID="cmdAtualiza" OnClick="cmdAtualiza_Click" runat="server" Width="71px" Height="24px" Text="Atualizar" />
        </div>
        <!-- Aqui esta o cartão: -->
        <asp:panel ID="pnlCartao" style="LEFT: 350px; POSITION: absolute; TOP: 16px; height: 528px;" runat="server" Width="340px" HorizontalAlign="Center"><br />&nbsp; 
            <asp:Label ID="lblTexto" runat="server" Width="256px" Height="150px" /><br /><br /><br />
            <asp:Image ID="imgDefault" runat="server" Width="259px" Height="217px" />
        </asp:panel>
    </form>
</body>
</html>

A página web esta dividida em duas regiões. À esquerda, há uma tag <div> comum contendo um conjunto de controles Web para especificar as opções do cartão. À direita está um controle Panel (chamado pnlCartao), que contém dois outros controles (lblTexto e imgDefault) que são usados para exibir o texto configurável pelo usuário e uma imagem. Este texto e imagem representa o cartão. Quando a página for carregada pela primeira vez, o cartão ainda não será gerado, e a parte direita estará em branco conforme vemos na figura abaixo:

Para obter o layout de duas colunas neste exemplo, temos duas escolhas:

1- usar tabelas HTML (que são uma técnica um tanto antiquada)
2- usar o posicionamento absoluto com estilos CSS (como neste exemplo).


O posicionamento absoluto é fácil de entender. Basta olhar para o atributo de
estilo no painel de controle, o que especifica uma coordenada superior e esquerda
fixa na página web. Quando o painel for processado para HTML, este ponto torna-se
seu canto superior esquerdo.

O elemento <div> é útil quando você quiser agrupar texto e controles e aplicar um conjunto de propriedades de formatação (tal como uma cor ou fonte) a todos eles. O elemento <div> também é uma ferramenta essencial para os blocos de posicionamento do conteúdo de uma página.

Sempre que o usuário clicar no botão Atualizar a página sofre um postback e o cartão é atualizado.

Vamos preencher os controles usando o código no arquivo Default.aspx.cs. Vamos tratar com os seguintes eventos:

O código code-behind do arquivo Default.aspx.cs é visto abaixo:

using System;
using System.Web.UI.WebControls;
using System.Drawing;

namespace GeradorDeCartoes
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
                if (!this.IsPostBack)
                {
                    // define as opções de cores
                    lstCorDeFundo.Items.Add("White");
                    lstCorDeFundo.Items.Add("Red");
                    lstCorDeFundo.Items.Add("Green");
                    lstCorDeFundo.Items.Add("Blue");
                    lstCorDeFundo.Items.Add("Yellow");
                    // define as opções de fonte
                    lstNomeDaFonte.Items.Add("Times New Roman");
                    lstNomeDaFonte.Items.Add("Arial");
                    lstNomeDaFonte.Items.Add("Verdana");
                    lstNomeDaFonte.Items.Add("Tahoma");
                    // define imagens
                    lstImagem.Items.Add("feliz");
                    lstImagem.Items.Add("aniversario1");
                    lstImagem.Items.Add("aniversario2");
                    lstImagem.Items.Add("anonovo1");
                    lstImagem.Items.Add("anonovo2");
                    lstImagem.Items.Add("maes1");
                    lstImagem.Items.Add("maes2");
                    lstImagem.Items.Add("namorados1");
                    lstImagem.Items.Add("namorados2");
                    lstImagem.Items.Add("pais1");
                    lstImagem.Items.Add("pais2");
                    lstImagem.Items.Add("amigos");
                    lstImagem.Items.Add("amizade");
                    lstImagem.SelectedIndex = -1;
                    // Define o estilo da borda adicionando uma série de objetos ListItem
                    ListItem item = new ListItem();
                    // O texto Item indica o nome da opção
                    item.Text = BorderStyle.None.ToString();
                    // O valor do item registra o inteiro correspondente
                    // a partir da enumeração. Para obter este valor você
                    // precisa dar um cast no valor da enumeração para integer
                    // e então converter o numero para uma string para ser colocada na pagina HTML
                    item.Value = ((int)BorderStyle.None).ToString();
                    // Adicona um item
                    lstBorda.Items.Add(item);
                    // repete o processo para as mais dois estilos de bordas
                    item = new ListItem();
                    item.Text = BorderStyle.Double.ToString();
                    item.Value = ((int)BorderStyle.Double).ToString();
                    lstBorda.Items.Add(item);
                    item = new ListItem();
                    item.Text = BorderStyle.Solid.ToString();
                    item.Value = ((int)BorderStyle.Solid).ToString();
                    lstBorda.Items.Add(item);
                    // Selecina o primeira opção da borda
                    lstBorda.SelectedIndex = 0;
                    // Defina a imagem
                    atualizaImagem();
                }
       }
       protected void cmdAtualiza_Click(object sender, EventArgs e)
       {
           // Atualiza a cor
           pnlCartao.BackColor = Color.FromName(lstCorDeFundo.SelectedItem.Text);
           // atualiza fonte
           lblTexto.Font.Name = lstNomeDaFonte.SelectedItem.Text;
           if (Int32.Parse(txtTamanhoDaFonte.Text) > 0)
           {
               lblTexto.Font.Size = FontUnit.Point(Int32.Parse(txtTamanhoDaFonte.Text));
           }
           // Atualiza o estilo da borda
           // convertemos o valor do item da lista de uma string em um inteiro
           // e depois convertemos esse valor para a enumeração BorderStyle
           int borderValue = Int32.Parse(lstBorda.SelectedItem.Value);
           pnlCartao.BorderStyle = (BorderStyle)borderValue;
           lblTexto.Text = txtTexto.Text;
           atualizaImagem();
      }

       protected void lstImagem_SelectedIndexChanged(object sender, EventArgs e)
       {  
           //chama a rotina para atualizar a fonte
           atualizaImagem();
       }

       protected void atualizaImagem()
       {
           //verifica se é para exibir a imagem
           if (chkImagem.Checked)
           {
               //obtem a imagem selecionada e exibe
               if (lstImagem.SelectedIndex != -1)
               {
                   string nomeImagem = lstImagem.SelectedItem.Text + ".jpg";
                   imgDefault.ImageUrl = @"~/Imagens/" + nomeImagem;
                   imgDefault.Visible = true;
               }
           }
           else
           {
               //não exibe a imagem
               imgDefault.Visible = false;
           }
       }
   }
}

Note que definimos a rotina atualizaImagem() que verifica se a imagem deve ser exibida e obtém o valor selecionado da dropdownlist de imagens montando o nome da imagem e exibindo-a no controle image imgDefault usando a propriedade ImageUrl.

Abaixo vemos o cartão montado e exibido com as opções selecionadas na página Default.aspx:

No evento Load da página verificamos se não esta ocorrendo um postback (!this.IsPostBack) e preenchemos os controles lstCorDeFundo, lstNomeDaFonte e lstImagem com valores pré-definidos.

Como você pode ver, este exemplo limita o usuário a escolher algumas fontes e cores pré-definidas no código.

O código para a opção BorderStyle é interessante. O controle lstBorda tem uma lista que exibe o nome do texto de um dos valores da enumeração BorderStyle, cada valor enumerado é um inteiro com um nome atribuído a ele. O lstBorda também armazena o número correspondente para que o código possa recuperar o número e definir a enumeração quando o usuário faz uma seleção e o evento cmdAtualizar_Click for disparado.

Melhorando o gerador de cartões

Nosso gerador de cartões esta funcional mas pode ser melhorado. Qualquer alteração feita pelo usuário somente será efetivada se o botão Atualizar for clicado. Vamos tornar o processo de gerar os cartões mais automático.

Vamos incluir também uma nova dropdownlist permitindo que o usuário selecione a cor da fonte. O código para isso é o seguinte:

Escolha a cor da fonte:<br />
<asp:dropdownlist ID="lstCorDaFonte" runat="server" Height="22px" Width="194px" AutoPostBack="True" onselectedindexchanged="ControlChanged"></asp:dropdownlist><br /><br />

Outro ponto que podemos melhorar é preencher os controles para as fontes e nome das fontes a partir das fontes instaladas no sistema ao invés de definirmos isso no código com algumas poucas opções.

Assim podemos preencher o controle lstNomeDaFonte com uma lista das fontes instaladas usando a classe InstalledFontCollection conforme o código a seguir:

InstalledFontCollection fontes = new InstalledFontCollection();
foreach (FontFamily familia in fontes.Families)
{
  lstNomeDaFonte.Items.Add(familia.Name);
}

Para obter uma lista dos nomes das cores, é preciso recorrer a um truque mais avançado. Embora você possa codificar uma lista de cores usando a enumeração System.Drawing.KnownColor. No entanto extrair os nomes a partir desta enumeração dá algum trabalho.

O truque é usar uma característica básica de todas as enumerações. O método estático Enum.GetNames(), que inspeciona uma enumeração e fornece um array de strings, com uma string para cada valor na enumeração. A página web pode, então, usar o databinding para preencher automaticamente o controle de lista com a informação no array de cores. Abaixo temos o código que faz isso:

string[] borderStyleArray = Enum.GetNames(typeof(BorderStyle));
lstBorda.DataSource = borderStyleArray;
lstBorda.DataBind();

Este código gera um novo desafio: como vamos converter o valor que o usuário seleciona para o a constante apropriada para a enumeração ?

Quando o usuário escolhe um estilo de borda da lista, a propriedade SelectedItem terá uma cadeia de texto como "Groove". Mas, para aplicar este estilo de borda para o controle, você precisa encontrar uma maneira de determinar a constante enumerada que corresponde a este texto.

Neste caso, a abordagem mais direta é usar um recurso avançado chamado TypeConverter. Um TypeConverter é uma classe especial que é capaz de converter de uma forma especializada (neste caso, a enumeração BorderStyle) para um tipo mais simples (como uma string), e vice-versa. Para acessar esta classe, você precisa importar o namespace System.ComponentModel:

using System.ComponentModel;

// Encontra o apropriado TypeConverter para a enumeração BorderStyle
TypeConverter converter =  TypeDescriptor.GetConverter(typeof(BorderStyle));

// atualiza o estilo da borda usando o valor a partir do  converter.
pnlCartao.BorderStyle = (BorderStyle)converter.ConvertFromString(lstBorda.SelectedItem.Text);

Este código obtém o TypeConverter adequado (neste caso, um que é concebido para funcionar com a enumeração BorderStyle). Em seguida, ele converte o nome de texto (como Solid) para o valor apropriado (BorderStyle.Solid).

O último passo é usar eventos postback automático da ASP.NET para fazer a atualização do cartão de forma dinâmica cada vez que uma opção for alterada. O botão Atualizar, agora poderia ser usado para enviar a versão final do cartão por email para um destinatário, ou acionar uma rotina para armazenar o cartão em um banco de dados.(fica a seu critério definir o que deseja fazer...)

Para configurar os controles de forma que eles acionem automaticamente uma postagem da página quando ocorrer qualquer alteração em um evento basta definir a propriedade AutoPostBack de cada controle de entrada para true.

Obs: Autopostback é o mecanismo pelo qual a página será postada de volta para o servidor automaticamente com base em alguns eventos nos controles do formulário web. Em alguns destes controles, se definirmos a propriedade AutoPostBack igual a true, a requisição será enviada para o servidor quando um evento ocorrer no controle.

A seguir vamos altear as tags dos controles de forma que a mudança do evento para cada controle seja vinculada para um tratamento de evento chamado ControlChanged que iremos definir depois no código.

Abaixo temos a versão final do arquivo Default.aspx com as novas implementações:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GeradorDeCartoes.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Gerador de Cartões</title>
</head>
<body>
    <form id="form1" runat="server">
        <div style="BORDER-RIGHT: thin ridge; PADDING-RIGHT: 20px; BORDER-TOP: thin ridge; PADDING-LEFT: 20px;
	        FONT-SIZE: x-small; PADDING-BOTTOM: 20px; BORDER-LEFT: thin ridge; WIDTH: 295px; PADDING-TOP: 20px;
                       BORDER-BOTTOM: thin ridge; FONT-FAMILY: fantasy; HEIGHT: 490px; BACKGROUND-COLOR: lightyellow">
            <!-- Controles do formulário: -->
            Escolha uma cor de fundo:<br />
            <asp:DropDownList ID="lstCorDeFundo" runat="server" AutoPostBack="True" onselectedindexchanged="ControlChanged" Width="194px" Height="22px"/><br /><br />
            Escolha a cor da fonte:<br />
           <asp:dropdownlist ID="lstCorDaFonte" runat="server" Height="22px" Width="194px" AutoPostBack="True" onselectedindexchanged="ControlChanged">
           </asp:dropdownlist><br /><br />
            Escolha a fonte:<br />
            <asp:DropDownList ID="lstNomeDaFonte" runat="server" AutoPostBack="True" onselectedindexchanged="ControlChanged" Width="194px" Height="22px" /><br /><br />
            Defina o tamanho da fonte:<br />
            <asp:TextBox ID="txtTamanhoDaFonte" runat="server" AutoPostBack="True" ontextchanged="ControlChanged" >10</asp:TextBox>
            <br /><br />
            Escolha um estilo de borda:<br />
            <asp:RadioButtonList ID="lstBorda" runat="server" AutoPostBack="True" onselectedindexchanged="ControlChanged" RepeatColumns="2" Width="177px" Height="59px" /><br />
            Escolha uma Imagem :<br />
            <asp:DropDownList ID="lstImagem" runat="server" AutoPostBack="True" Width="194px" Height="22px" OnSelectedIndexChanged="ControlChanged" /><br /><br />
            <asp:CheckBox ID="chkImagem" runat="server" AutoPostBack="True" oncheckedchanged="ControlChanged" Text="Exibir Imagem"></asp:CheckBox><br /><br />
            Digite o texto do cartão abaixo:<br />
            <asp:TextBox ID="txtTexto" runat="server" AutoPostBack="True" ontextchanged="ControlChanged" Width="240px" Height="85px" TextMode="MultiLine" >Seu Texto aqui</asp:TextBox>
            <br /><br />
            <asp:Button ID="cmdAtualiza" OnClick="cmdAtualiza_Click" runat="server" Width="71px" Height="24px" Text="Atualizar" />
        </div>
        <!-- Aqui esta o cartão: -->
        <asp:panel ID="pnlCartao" style="LEFT: 350px; POSITION: absolute; TOP: 16px; height: 528px;" runat="server" Width="340px" HorizontalAlign="Center"><br />&nbsp; 
            <asp:Label ID="lblTexto" runat="server" Width="256px" Height="150px" /><br /><br /><br />
            <asp:Image ID="imgDefault" runat="server" Width="259px" Height="217px" />
        </asp:panel>
    </form>
</body>
</html>

Note que o nome do evento Changed vai depender do controle. Assim a caixa de texto proporciona um evento TextChanged, o ListBox fornece um evento SelectedIndexChanged, e assim por diante.

Finalmente, você precisa criar um manipulador de eventos que possa lidar com os eventos de mudança. Podemos usar o mesmo manipulador de evento para todos os controles de entrada. Todo o manipulador de evento precisa vincular a rotina de atualização que regenera o cartão.

O novo código do code-behind em Default.aspx.cs é visto abaixo:

using System;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Text;
using System.ComponentModel;

namespace GeradorDeCartoes
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
                if (!this.IsPostBack)
                {
                    // Pega a lista de cores
                    string[] colorArray = Enum.GetNames(typeof(KnownColor));
                    lstCorDeFundo.DataSource = colorArray;
                    lstCorDeFundo.DataBind();

                    //define a cor da fonte
                    lstCorDaFonte.DataSource = colorArray;
                    lstCorDaFonte.DataBind();
                    lstCorDaFonte.SelectedIndex = 34;
                    lstCorDaFonte.SelectedIndex = 163;

                    // define imagens
                    lstImagem.Items.Add("feliz");
                    lstImagem.Items.Add("aniversario1");
                    lstImagem.Items.Add("aniversario2");
                    lstImagem.Items.Add("anonovo1");
                    lstImagem.Items.Add("anonovo2");
                    lstImagem.Items.Add("maes1");
                    lstImagem.Items.Add("maes2");
                    lstImagem.Items.Add("namorados1");
                    lstImagem.Items.Add("namorados2");
                    lstImagem.Items.Add("pais1");
                    lstImagem.Items.Add("pais2");
                    lstImagem.Items.Add("amigos");
                    lstImagem.Items.Add("amizade");
                    lstImagem.SelectedIndex = -1;

                    // pega a lista de fontes disponíveis
                    InstalledFontCollection fonts = new InstalledFontCollection();
                    foreach (FontFamily family in fonts.Families)
                    {
                        lstNomeDaFonte.Items.Add(family.Name);
                    }

                    // Defina o estilo da borda
                    string[] borderStyleArray = Enum.GetNames(typeof(BorderStyle));
                    lstBorda.DataSource = borderStyleArray;
                    lstBorda.DataBind();

                    // Selecina a primeira opção
                    lstBorda.SelectedIndex = 0;
                }
       }

       protected void cmdAtualiza_Click(object sender, EventArgs e)
       {
           AtualizaCartao();
       }

       protected void AtualizaCartao()
       {
           // Atualiza a cor
           pnlCartao.BackColor = Color.FromName(lstCorDeFundo.SelectedItem.Text);
           lblTexto.ForeColor = Color.FromName(lstCorDaFonte.SelectedItem.Text);

           // Atualiza fonte
           lblTexto.Font.Name = lstNomeDaFonte.SelectedItem.Text;
           try
           {
               if (Int32.Parse(txtTamanhoDaFonte.Text) > 0)
               {
                   lblTexto.Font.Size = FontUnit.Point(Int32.Parse(txtTamanhoDaFonte.Text));
               }
           }
           catch
           {
               lblmsg.Text = "Tamanho da fonte inválido !";
           }

           try
           {
               if (Int32.Parse(txtTamanhoDaFonte.Text) > 0)
               {
                   lblTexto.Font.Size = FontUnit.Point(Int32.Parse(txtTamanhoDaFonte.Text));
               }
           }
           catch
           {
               lblmsg.Text = "Tamanho da fonte inválido !";
           }

           // localiza o TypeConverter adequado para a enumeração BorderStyle
           TypeConverter cnvrt = TypeDescriptor.GetConverter(typeof(BorderStyle));

           // Atualiza o estilo da borda usando o avlor do converter.
           pnlCartao.BorderStyle = (BorderStyle)cnvrt.ConvertFromString(lstBorda.SelectedItem.Text);

           atualizaImagem();

           // Define o texto
           lblTexto.Text = txtTexto.Text;
       }


       protected void lstImagem_SelectedIndexChanged(object sender, EventArgs e)
       {  
           //chama a rotina para atualizar a fonte
           atualizaImagem();
       }

       protected void atualizaImagem()
       {
           //verifica se é para exibir a imagem
           if (chkImagem.Checked)
           {
               //obtem a imagem selecionada e exibe
               if (lstImagem.SelectedIndex != -1)
               {
                   string nomeImagem = lstImagem.SelectedItem.Text + ".jpg";
                   imgDefault.ImageUrl = @"~/Imagens/" + nomeImagem;
                   imgDefault.Visible = true;
               }
           }
           else
           {
               //não exibe a imagem
               imgDefault.Visible = false;
           }
       }

       protected void ControlChanged(Object sender, EventArgs e)
       {
           // Atualiza o texto do cartão 
           AtualizaCartao();
       }
   }
}

Executando o projeto veremos que a atualização do cartão agora é automática. Basta selecionar qualquer opção que a alteração será visualizada de imediato.

Com isso concluímos o nosso exemplo onde recordamos a utilização de alguns controles Web Forms, seus eventos e como tratá-los via código.

Obs: Você deve tomar cuidado com a utilização da propriedade AutoPostBack visto que dependendo do cenário isso pode causar problemas em sua aplicação.

Pegue o projeto completo aqui: GeradorDeCartoes.zip

Mat 8:19 E, aproximando-se um escriba, disse-lhe: Mestre, seguir-te- ei para onde quer que fores.

Mat 8:20 Respondeu-lhe Jesus: As raposas têm covis, e as aves do céu têm ninhos; mas o Filho do homem não tem onde reclinar a cabeça.

Mat 8:21 E outro de seus discípulos lhe disse: Senhor, permite-me ir primeiro sepultar meu pai.

Mat 8:22 Jesus, porém, respondeu-lhe: Segue-me, e deixa os mortos sepultar os seus próprios mortos.

Referências:


José Carlos Macoratti