Em todos os sistemas web existem vários problemas que não são vistos pelos programadores e testers, e sim pelos usuários do sistema. Um dos problemas mais graves que acontece e que poucos conseguem perceber é quando um usuário clica duas vezes em um botão para gravar um registro no banco e o sistema acaba gravando duas vezes o mesmo registro. Este artigo descreve uma resolução simples e prática para aplicar em seu sistema .NET utilizando o Microsoft Visual Studio 2008 e um simples JavaScript.

Registro duplicado

A aplicação web é executada no servidor, a cada acesso o servidor processas a página solicitada e envia somente o HTML para o cliente. Cada vez que você clica em um botão a página dispara um evento que será processado no servidor que em seguida retorna a página toda para o cliente. Esse processo é chamado de POST. Por esse motivo todo POST que ocorre a página “pisca”.

Existe um tempo em que a página ainda fica em exibição no browser antes de piscar. Esse é o tempo em que a aplicação leva para enviar a requisição ao servidor, o servidor por sua vez processar a informação e retornar o HTML. E também existe outro tempo que o browser fica em branco, que é o tempo em que o HTML é renderizado no browser.

Quando o usuário clica uma vez e no botão a página envia a requisição ao servidor e ainda mostra a página, aí o usuário clica outra vez no botão e envia outra requisição ao browser. O servidor recebendo duas requisições irá gravar duas vezes o mesmo registro.

Solução

Para solucionar esse problema, criei um JavaScript que em todo POST é acrescentado um div na frente da página toda e enquanto esse div estiver aparecendo não dará outro POST. Assim bloqueia o usuário clicar duas vezes com o mouse ou mesmo apertar duas vezes a tecla enter. A diferença dessa solução apresentada para o UpdateProgress, recurso presente no Microsoft Ajax .NET, é que o UpdateProgress só atua no UpdatePanel enquanto essa solução do Aguarde atua em qualquer post da sua solução sem retrabalho em todas as páginas.

Então vamos ao que interessa. O código, seguindo os passos.

Crie um novo website no Visual Studio. Chamei a minha de TesteMSGAguarde.

Crie uma pasta chamada JS dentro do seu WebSite e nessa pasta adicione um novo arquivo JScript chamado MSGAguarde.js e adicione o seguinte código:


function avisoAguarde()
{
	if(document.getElementById('divProcessando'))
	{
		document.getElementById('divProcessando').style.display='';
		return;
	}
	oDiv = document.createElement("div");
	with (oDiv)
	{
		id = "divProcessando";
	}
	document.body.appendChild(oDiv);
}

O código acima verifica se o div já existe, caso sim, o torna visível, caso não, cria um novo.

Agora crie um arquivo de classe no seu WebSite chamado BaseWebUi.cs (se seu site for em C#), cujo código será o seguinte:


public class BaseWebUi : System.Web.UI.Page
{
	protected override void OnInit(EventArgs e)
	{
		//se o div de Aguarde ainda estiver mostrando ele tira
		ScriptManager src = ScriptManager.GetCurrent(Page);
		if (src != null)
			ScriptManager.RegisterClientScriptBlock(
				this,
				typeof(void),
				"TiraDivAguarde",
				"if(document.getElementById('divProcessando')) 
				document.getElementById('divProcessando')
				.style.display = 'none';",
				true);
		else
			ClientScript.RegisterStartupScript(
				typeof(Page),
				"TiraDivAguarde",
				"if(document.getElementById('divProcessando')) 
				document.getElementById('divProcessando')
				.style.display = 'none';",
				true);

		ClientScript.RegisterOnSubmitStatement(
			this.GetType(),
			"zerarfiltro",
			"if(document.getElementById('divProcessando') && 
			document.getElementById('divProcessando')
			.style.display!='none')return false;");

		ClientScript.RegisterOnSubmitStatement(
			this.GetType(),
			"Aguarde",
			"if (typeof(ValidatorOnSubmit) == 'function' && 
			ValidatorOnSubmit() == false) return false; avisoAguarde();");
			
		base.OnInit(e);
	}
}

A classe herda da System.Web.UI.Page e sobrescreve o método OnInit (que é executado toda vez que a página é carregada). O método onInit chama a função javascript avisoAguarde() e impede o post se o divProcessando estiver na tela.

Agora é só referenciar o arquivo JS e adaptar o BaseWebUi à sua página. Abra o arquivo Default.aspx.cs (se seu site for em C#) e troque a herança de System.Web.Ui.Page para BaseWebUi.

Abra o arquio Default.aspx e entre as tags Head coloque a referência ao arquivo MSGAguarde.js, da seguinte forma:


<script type="text/javascript" src="js/MSGAguarde.js"></script>

Agora vamos dar um visual para o DIV como Theme. Crie um Asp.Net Folderdo tipo Theme, chamado MSG. Adicione uma pasta chamada Imagens em seu Theme MSG e nessa mesma pasta, copie um GIF animado com uma mensagem de aguarde de sua preferência. Em seguida adicione um novo arquivo CSS em seu Theme MSG. A estrutura de pastas deve ficar semelhante à Figura 1.

Theme com imagem e CSS
Figura 1. Theme com imagem e CSS

E escreva um Theme para o DIV que aparecerá na frente da tela a cada post. Neste exemplo foi utilizado o seguinte CSS:


#divProcessando
{
	background: url(imagens/carregandopagina.gif) no-repeat center;
	position:fixed;
	z-index:99;
	width:100%;
	height:100%;
	top:0;
	left:0;
}

Adicione o Theme MSG para toda a sua aplicação. Abra o arquivo Web.Config e na tag adicione a propriedadetheme=”MSG”.

Pronto! Agora todo post que a sua página fizer irá aparecer a imagem de Aguarde.

Teste

Para testar nossa aplicação crie um botão do asp.net na Default.aspx e no evento Click, digite o seguinte código:


System.Threading.Thread.Sleep(1000);

O código serve para quando o evento Click do botão for acionado, o sistema espera um segundo, para efeito de emular um processamento mais longo. Execute a página e clique no botão e a seguinte mensagem aparecerá para avisar que o sistema está processando.

Aplicação em execução
Figura 2. Aplicação em execução

Um problema simples pode destruir um projeto. Essa solução é simples de implementar e funciona quando a página possui validators ou até mesmo Ajax, isso dá muito mais qualidade ao software. E lembre-se sempre, o cliente é fiel a qualidade e não à empresa.