IntroduçãoMuitas
vezes é útil exibir mensagens de "aguarde" ou imagens GIF animadas durante o processo de envio de formulário de um aplicativo da web, especialmente quando o processo de envio leva muito tempo. Recentemente, desenvolvi um programa de envio de pesquisas onde usuários internos carregam planilhas do Excel por meio de uma página da web. O programa insere os dados da planilha carregada no banco de dados. Esse processo leva apenas alguns segundos, mas mesmo que leve alguns segundos, é um processo de espera muito óbvio do ponto de vista da página da web. Durante o teste do programa, alguns usuários clicaram repetidamente no botão de upload. Portanto, é útil fornecer uma mensagem visual para informar às pessoas que o upload está em andamento. E oculte o botão de upload ao mesmo tempo para evitar cliques múltiplos. O controle apresentado aqui é uma subclasse do controle Button, que demonstra como encapsular o código JavaScript do lado do cliente em um controle de servidor ASP.NET para fornecer funções convenientes.
Embora existam muitos exemplos de javascript para fazer isso, encontrei alguns problemas quando tentei encapsular essas funções em controles ASP.NET. Primeiro tentei invalidar o botão por meio do manipulador javascript onclick e substituí-lo por outro texto. Mas achei muito complicado, pois isso iria atrapalhar o funcionamento do evento click no lado do servidor asp.net. O que finalmente funcionou e teve um bom suporte para diferentes navegadores foi renderizar o botão em uma tag div. O div pode estar oculto e fora do caminho do evento de clique do asp.net.
Usando o controle
Como um derivado do controle de botão normal, as funções de PleaseWaitButton são basicamente as mesmas. Ele usa três propriedades adicionais para gerenciar a exibição de mensagens ou imagens "Aguarde" quando o botão é clicado.
Por favor, aguardeTexto
Esta é a mensagem de texto do lado do cliente exibida e, se presente, substituirá o botão quando ele for clicado.
Por favor, aguardeImagem
Este é o arquivo de imagem (como uma imagem gif animada) que é exibido e, se presente, substituirá o botão quando ele for clicado. Este atributo se tornará o atributo src na tag <img>.
Por favorAguardeTipo
Um dos valores de enumeração PleaseWaitTypeEnum: TextOnly, ImageOnly, TextThenImage ou ImageThenText. Ele controla o layout de mensagens e imagens.
Abaixo está um exemplo de arquivo .aspx que demonstra um PleastWaitButton com PleaseWaitText e PleaseWaitImage definidos.
<%@ Idioma da página="C#" %>
<%@ Registrar TagPrefix="cc1" Namespace="JavaScriptControls"
Assembly="Por favorWaitButton" %>
<script runat="servidor">
private void PleaseWaitButton1_Click (objeto remetente, System.EventArgs e)
{
// Manipulador de eventos Click do lado do servidor;
// simula algo que pode levar muito tempo,
// como upload de arquivo ou processamento demorado do servidor
DateTime dt = DateTime.Now.AddSeconds(5);
enquanto (DateTime.Now <dt)
{
// não faz nada; simula uma pausa de 5 segundos
}
// no final do loop exibe uma mensagem de sucesso
// e oculta o formulário de envio
painelSuccess.Visible = verdadeiro;
PleaseWaitButton1.Visible = falso;
}
</script>
<html>
<cabeça>
<title>Testando PleaseWaitButton</title>
</head>
<corpo>
<form id="Form1" method="post" runat="servidor">
<P>Testando o controle PleaseWaitButton.</p>
<cc1:PleaseWaitButton id="PleaseWaitButton1" runat="servidor"
Text="Clique em mim para iniciar um processo demorado"
PleaseWaitText="Por favor, aguarde"
PleaseWaitImage="pleaseWait.gif"
OnClick="Por favorWaitButton1_Click" />
<asp:Panel id="panelSuccess" runat="servidor"
visível="falso">
Obrigado por enviar este formulário. Você é realmente.
o usuário mais legal que já tive o prazer de servir.
Não, sério, estou falando sério. Houve outros, claro,
mas você está realmente em uma aula sozinho.
</asp:Painel>
</form>
</body>
</html>
Como funciona
O controle PleaseWaitButton renderiza um botão ASP.NET padrão dentro de uma tag <div>. Ele também renderiza uma tag <div> vazia para a informação/imagem. Ao clicar no botão, a ocultação do botão e a exibição das informações são controladas pela função Javascript (ver função cliente abaixo). Por conveniência, a implementação de todo o código do cliente javascript necessário é feita pelo controle de servidor PleaseWaitButton.
Como PleaseWaitButton implementa seu próprio manipulador onclick javascript, precisamos executar algumas etapas extras para preservar o manipulador onclick original e permitir que o controle execute de forma limpa algum código de validação do lado do cliente. Para atingir esse objetivo, primeiro restauramos a classe base Button para um buffer de string e, em seguida, manipulamos-a habilmente para incluir o código onclick que definimos.
substituição protegida void Render (saída HtmlTextWriter)
{
// Gera o html do botão (com atributos)
// para um HtmlTextWriter fictício
StringWriter sw = new StringWriter();
HtmlTextWriter wr = novo HtmlTextWriter(sw);
base.Render(wr);
string sButtonHtml = sw.ToString();
wr.Fechar();
sw.Fechar();
// agora modifique o código para incluir um manipulador "onclick"
// com nossa função PleaseWait() chamada apropriadamente
// após qualquer validação do lado do cliente.
sButtonHtml = ModifyJavaScriptOnClick(sButtonHtml);
// antes de renderizar o botão, gera um <div> vazio
// que será preenchido no lado do cliente via javascript
// com uma mensagem "aguarde""
saída.Write(string.Format("<div id='pleaseWaitButtonDiv2_{0}'>",
this.ClientID));
output.Write("</div>");
// renderiza o botão em uma tag <div> de encapsulamento própria
saída.Write(string.Format("<div id='pleaseWaitButtonDiv_{0}'>",
this.ClientID));
saída.Write(sButtonHtml);
saída.Write("</div>");
}
Essa técnica de reduzir o botão a um buffer de string e então processar seu conteúdo onclick é certamente um hack, mas nos permite implementar o código de validação padrão na classe do botão pai e então implementar nossa chamada de função Javascript PleaseWait(). Sem fazer isso, teríamos que implementar nossa chamada de função PleaseWait() no atributo onclick antes do código de validação, a menos que estivéssemos dispostos a substituir completamente a renderização do atributo da classe Button pai. Desta forma, mesmo que haja erros de entrada na página, terá o efeito de ocultar o botão e exibir a mensagem “aguarde” que não desejamos. Portanto, devemos forçar a função do cliente PleaseWait() no manipulador onclick a aparecer após a validação da página do cliente.
A modificação do atributo onclick ocorre na função ModifyJavaScriptOnClick(). Esta função obtém a string HTML renderizada pelo botão e verifica se o atributo onclick está presente. Nesse caso, esta função verifica se o código de validação do lado do cliente é usado. Se for esse o caso, a função PleaseWait() que definimos será adicionada no final do código onclick existente, imediatamente após a variável boolin Page_IsValid verificada pelo cliente. Esta variável representa se o controle de validação é usado. Se o valor de Page_IsValid for falso, a mensagem “Aguarde” não será exibida. Exibido se for Verdadeiro.
string privada ModifyJavaScriptOnClick(string sHtml)
{
// Agradecimentos ao membro do CodeProject KJELLSJ (Kjell-Sverre Jerijaervi)
// para ideias de código para permitir que o botão funcione com validação do lado do cliente
string sReturn = "";
string sPleaseWaitCode = GeneratePleaseWaitJavascript();
// existe um atributo onclick existente?
Regex rOnclick = new Regex("onclick="(?<onclick>[^"]*)");
Corresponder mOnclick = rOnclick.Match(sHtml);
if (mOnclick.Sucesso)
{
// existe um atributo onclick existente;
// adicione nosso código ao final dele;
// a validação foi renderizada, certifique-se
// verificamos se a página é válida;
string sExisting = mOnclick.Groups["onclick"].Value;
string sReplace = sExistente
+ (sExisting.Trim().EndsWith(";") ? "" : "; ");
if (IsValidatorIncludeScript() && this.CausesValidation)
{
//inclui código para verificar se a página é válida
string sCode = "if (Page_IsValid) " + sPleaseWaitCode
+ "retornar Página_IsValid;";
// adiciona nosso código ao final do código onclick existente;
sReplace = sReplace + sCode;
}
outro
{
// não se preocupe com a validade da página;
sReplace = sReplace + sPleaseWaitCode;
}
// agora substitua nosso código onclick
sReplace = "onclick="" + sReplace;
sReturn = rOnclick.Replace(sHtml, sReplace);
}
outro
{
// não existe um atributo onclick existente;
//adiciona o nosso
int i = sHtml.Trim().Comprimento - 2;
string sInsert = " onclick="" + sPleaseWaitCode + "" ";
sReturn = sHtml.Insert(i, sInsert);
}
return sReturn;
}
O IsValidatorIncludeScript() usa a verificação acima para ver se existe um bloco de código Javascript padrão para o controle de validação do asp.net registrado na página. A seguir, é usado um método simples para testar se há código de validação e variáveis como Page_IsValid.
bool privado IsValidatorIncludeScript()
{
// retorna TRUE se esta página tiver javascript registrado
// para validação do lado do cliente; este código não pode ser registrado;
// se o ASP.NET detecta o que pensa (correta ou incorretamente)
// é um navegador de nível inferior
return this.Page.IsStartupScriptRegistered("ValidatorIncludeScript");
} O GeneratePleaseWaitJavascript() a seguir constrói a função Javascript PleaseWait() contida no atributo onclick. Podemos determinar o layout desejado inspecionando as propriedades do controle.
string privada GeneratePleaseWaitJavascript()
{
// cria uma chamada de função JavaScript "PleaseWait()"
// adequado para uso em um manipulador de eventos onclick
string sMessage = "";
string sText = _pleaseWaitText;
string sImage = (_pleaseWaitImage! = String.Empty
?string.Format(
"<img src="{0}" alinhamento="absmiddle" alt="{1}"/>"
, _pleaseWaitImage, _pleaseWaitText )
: String.Empty);
// estabelece o layout baseado em PleaseWaitType
mudar (_pleaseWaitType)
{
caso PleaseWaitTypeEnum.TextThenImage:
sMessage = sText + sImage;
quebrar;
caso PleaseWaitTypeEnum.ImageThenText:
sMessage = sImage + sText;
quebrar;
caso PleaseWaitTypeEnum.TextOnly:
sMessage = sText;
quebrar;
caso PleaseWaitTypeEnum.ImageOnly:
sMessage = sImage;
quebrar;
}
// retorna o pedaço de código final
string sCode = string.Format(
"Por favor, espere('por favorWaitButtonDiv_{0}',
'por favorWaitButtonDiv2_{1}', '{2}');"
, this.ClientID, this.ClientID, sMessage);
sCode = sCode.Replace(""", """);
return sCode;
}
Se você especificar um PleaseWaitImage, deverá incluir um trecho adicional de código Javascript para notificar o cliente para pré-carregar a imagem. O registro deste script deverá aparecer no método OnPreRender substituído. A chave registrada é o nome da imagem; se vários botões usarem a mesma imagem, o script de pré-carregamento só precisará ser implementado uma vez. Uma expressão regular é usada aqui para criar uma variável de imagem Javascript para garantir que caracteres especiais (como barras em caminhos de arquivo) sejam convertidos em sublinhados.
substituição protegida void OnPreRender (EventArgs e)
{
base.OnPreRender (e);
// Se estivermos usando uma imagem, registre algum javascript
// para pré-carregamento de imagem do lado do cliente
if (_pleaseWaitImage! = String.Empty
&& _pleaseWaitType != PleaseWaitTypeEnum.TextOnly)
RegisterJavascriptPreloadImage(_pleaseWaitImage);
}
private void RegisterJavascriptPreloadImage(string sImage)
{
Regex rex = novo Regex("[^a-zA-Z0-9]");
string sImgName = "img_" + rex.Replace(sImage, "_")
;
sb.Append("<linguagem de script='JavaScript'>");
sb.Append("if (document.images) { ");
sb.AppendFormat("{0} = nova imagem();", sImgName);
sb.AppendFormat("{0}.src = "{1}";", sImgName, sImage);
sb.Append(" } ");
sb.Append("</script>");
this.Page.RegisterClientScriptBlock(sImgName + "_PreloadScript",
sb.ToString());
}
Funções do lado do cliente
O arquivo de texto incorporado javascript.txt contém o <div> que oculta o botão e o código do lado do cliente que exibe a mensagem ou imagem "aguarde". Esses códigos são carregados no método privado RegisterJavascriptFromResource() chamado no método OnInit() substituído. Este método chama o método genérico GetEmbeddedTextFile(), que carrega o arquivo como fonte e retorna o conteúdo como uma string.
substituição protegida void OnInit (EventArgs e)
{
base.OnInit(e);
// o código javascript do lado do cliente é mantido
// em um recurso incorporado; carregue o script;
// e registre-o na página.
RegisterJavascriptFromResource();
}
privado vazio RegisterJavascriptFromResource()
{
// carrega o arquivo de texto incorporado "javascript.txt"
// e registra seu conteúdo como script do lado do cliente
string sScript = GetEmbeddedTextFile("javascript.txt");
this.Page.RegisterClientScriptBlock("Por favorWaitButtonScript", sScript);
}
string privada GetEmbeddedTextFile(string sTextFile)
{
//função genérica para recuperar o conteúdo
// de um recurso de arquivo de texto incorporado como uma string
// obteremos o assembly em execução e derivaremos
// o namespace usando o primeiro tipo no assembly
Montagem a = Assembly.GetExecutingAssembly();
String sNamespace = a.GetTypes()[0].Namespace;
// com o assembly e o namespace, obteremos o
//recurso incorporado como um fluxo
Fluxo s = a.GetManifestResourceStream(
string.Format("{0}.{1}",sNamespace,sTextFile)
);
// lê o conteúdo do stream em uma string
StreamReader sr = novos StreamReader(s);
String sContents = sr.ReadToEnd()
;
s.Close();
return sConteúdo;
}
O recurso incorporado javascript.txt contém o método do lado do cliente PleaseWait() que é executado no manipulador Javascript onclick do botão. Este código também chama um método do lado do cliente HideDiv() para ocultar o contêiner <div> do botão e, em seguida, reúne as informações ou imagem na tag <div> anteriormente vazia, definindo o atributo innerHTML. A função auxiliar GetDiv() retorna um objeto <div> com id verificando document.getElementById, document.all e document.layers, garantindo compatibilidade com diferentes navegadores. Abaixo está o código completo do javascript.txt:
<linguagem script="JavaScript">
função GetDiv(sDiv)
{
vardiv;
se (document.getElementById)
div = document.getElementById(sDiv);
senão se (documento.all)
div = eval("janela." + sDiv);
senão se (document.layers)
div = document.layers[sDiv];
outro
div = nulo;
retornar div;
}
função HideDiv(sDiv)
{
d = GetDiv(sDiv);
se(d)
{
if (document.layers) d.visibility = "ocultar";
senão d.style.visibility = "oculto";
}
}
função PleaseWait(sDivButton, sDivMessage, sInnerHtml)
{
HideDiv(sDivButton);
var d = GetDiv(sDivMessage);
se (d) d.innerHTML = sInnerHtml;
}
</script>
Link original: http://www.codeproject.com/aspnet/PleaseWaitButton.asp
Baixe o projeto fonte - 7 Kb
Baixe o projeto de demonstração - 30 Kb
http://www.cnblogs.com/jeffamy/archive/2006/08/20/481952.html