Introduction
在web application的表单提交过程中显示“please wait”信息或者是gif动画图片通常是很有用的,特别是提交过程比较久的情况。我最近开发了一个调查提交程序,在程序里内部用户通过一个网页上传excel电子表格。程序将上传的电子表格数据插入到数据库中。这个过程只需要几秒钟,但即便是几秒钟,在网页是看来却是非常明显的等待过程。在程序测试的时候,一些用户重复地点击上传按钮。因此,提供一个视觉的信息来告诉人们上传正在进行中是很有用的。并同时把上传按钮一起隐藏掉,以防止多次点击。这里介绍的控件是Button控件的子类,它演示了如何把客户端javascript代码封装在asp.net服务器控件中来提供便利的功能。
虽然外面已经有很多javascript的例子来完成这件事情,但当我试图把这些功能封装到asp.net控件中时我发现了一些问题。我最开始尝试通过javascript的onclick句柄来使button无效,并用另外的文本取代。但我发现很棘手,这样会妨碍到asp.net服务器端的click事件的功能。而最终行得通的,并且对不同浏览器也有很好支持的方法是,让button在div标记中呈现。div可以隐藏并且不妨碍asp.net的click事件。
Using the control
作为正常的button控件的派生,PleaseWaitButton的功能与它基本一样。它通过三个附加的属性来管理当按钮被点击后"please Wait"信息或图片的显示。
PleaseWaitText
这是显示的客户端文本信息,如果存在,当按钮被点击它将取代按钮。
PleaseWaitImage
这是显示的图像文件(比如gif动画图像),如果存在,当按钮被点击它将取代按钮。这个属性将变成标记中的src属性。
PleaseWaitType
PleaseWaitTypeEnum枚举值之一:TextOnly,ImageOnly,TextThenImage,或者ImageThenText。它控制消息和图片的布局。
下面是一个.aspx文件示例,它演示了一个设置了PleaseWaitText和PleaseWaitImage的PleastWaitButton。
<%@ Page language="C#" %>
<%@ Register TagPrefix="cc1" Namespace="JavaScriptControls"
Assembly="PleaseWaitButton" %>
How It Works
PleaseWaitButton控件在
protected override void Render(HtmlTextWriter output)
{
// Output the button's html (with attributes)
// to a dummy HtmlTextWriter
StringWriter sw = new StringWriter();
HtmlTextWriter wr = new HtmlTextWriter(sw);
base.Render(wr);
string sButtonHtml = sw.ToString();
wr.Close();
sw.Close();
// now modify the code to include an "onclick" handler
// with our PleaseWait() function called appropriately
// after any client-side validation.
sButtonHtml = ModifyJavaScriptOnClick(sButtonHtml);
// before rendering the button, output an empty
// render the button in an encapsulating
string sReturn = "";
string sPleaseWaitCode = GeneratePleaseWaitJavascript();
// is there an existing onclick attribute?
Regex rOnclick = new Regex("onclick="(?
Match mOnclick = rOnclick.Match(sHtml);
if (mOnclick.Success)
{
// there is an existing onclick attribute;
// add our code to the end of it; if client-side
// validation has been rendered, make sure
// we check to see if the page is valid;
string sExisting = mOnclick.Groups["onclick"].Value;
string sReplace = sExisting
+ (sExisting.Trim().EndsWith(";") ? "" : "; ");
if (IsValidatorIncludeScript() && this.CausesValidation)
{
// include code to check if the page is valid
string sCode = "if (Page_IsValid) " + sPleaseWaitCode
+ " return Page_IsValid;";
// add our code to the end of the existing onclick code;
sReplace = sReplace + sCode;
}
else
{
// don't worry about the page being valid;
sReplace = sReplace + sPleaseWaitCode;
}
// now substitute our onclick code
sReplace = "onclick="" + sReplace;
sReturn = rOnclick.Replace(sHtml, sReplace);
}
else
{
// there isn't an existing onclick attribute;
// add ours
int i = sHtml.Trim().Length - 2;
string sInsert = " onclick="" + sPleaseWaitCode + "" ";
sReturn = sHtml.Insert(i, sInsert);
}
return sReturn;
}
这个IsValidatorIncludeScript() 利用上面的检查来查看是否有使用页面注册的asp.net验证控件的标准Javascript代码块。下面则用一个简单的方法测试了是否有验证代码和像Page_IsValid的变量存在。
private bool IsValidatorIncludeScript()
{
// return TRUE if this page has registered javascript
// for client-side validation; this code may not be registered
// if ASP.NET detects what it thinks (correctly or incorrectly)
// is a down-level browser.
return this.Page.IsStartupScriptRegistered("ValidatorIncludeScript");
} 下面这个GeneratePleaseWaitJavascript()构建了包含在onclick属性中的PleaseWait() Javascript函数。我们可以通过检查控件的属性来决定想要的布局。
private string GeneratePleaseWaitJavascript()
{
// create a JavaScript "PleaseWait()" function call
// suitable for use in an onclick event handler
string sMessage = "";
string sText = _pleaseWaitText;
string sImage = (_pleaseWaitImage != String.Empty
? string.Format(
""
, _pleaseWaitImage, _pleaseWaitText )
: String.Empty);
// establish the layout based on PleaseWaitType
switch (_pleaseWaitType)
{
case PleaseWaitTypeEnum.TextThenImage:
sMessage = sText + sImage;
break;
case PleaseWaitTypeEnum.ImageThenText:
sMessage = sImage + sText;
break;
case PleaseWaitTypeEnum.TextOnly:
sMessage = sText;
break;
case PleaseWaitTypeEnum.ImageOnly:
sMessage = sImage;
break;
}
// return the final code chunk
string sCode = string.Format(
"PleaseWait('pleaseWaitButtonDiv_{0}',
'pleaseWaitButtonDiv2_{1}', '{2}');"
, this.ClientID, this.ClientID, sMessage);
sCode = sCode.Replace(""", """);
return sCode;
}
如果指定了一个PleaseWaitImage,就必须包含额外的一段Javascript代码来通知客户端预载该图像。这段脚本的注册应该出现在重写的OnPreRender方法中。注册的键是图像的名称;如果多个按钮都使用同一图像,预载脚本只需要实施一次。这里使用了一个正则表达式来创建Javascript图像变量,以保证特殊字字符(比如文件路径中的斜线)转化成下划线。
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender (e);
// If we're using an image, register some javascript
// for client-side image preloading
if (_pleaseWaitImage != String.Empty
&& _pleaseWaitType != PleaseWaitTypeEnum.TextOnly)
RegisterJavascriptPreloadImage(_pleaseWaitImage);
}
private void RegisterJavascriptPreloadImage(string sImage)
{
Regex rex = new Regex("[^a-zA-Z0-9]");
string sImgName = "img_" + rex.Replace(sImage, "_");
StringBuilder sb = new StringBuilder();
sb.Append("");
this.Page.RegisterClientScriptBlock(sImgName + "_PreloadScript",
sb.ToString());
}
Client-side functions
嵌入的文本文件javascript.txt包含了隐藏按钮的
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// the client-side javascript code is kept
// in an embedded resource; load the script
// and register it with the page.
RegisterJavascriptFromResource();
}
private void RegisterJavascriptFromResource()
{
// load the embedded text file "javascript.txt"
// and register its contents as client-side script
string sScript = GetEmbeddedTextFile("javascript.txt");
this.Page.RegisterClientScriptBlock("PleaseWaitButtonScript", sScript);
}
private string GetEmbeddedTextFile(string sTextFile)
{
// generic function for retrieving the contents
// of an embedded text file resource as a string
// we'll get the executing assembly, and derive
// the namespace using the first type in the assembly
Assembly a = Assembly.GetExecutingAssembly();
String sNamespace = a.GetTypes()[0].Namespace;
// with the assembly and namespace, we'll get the
// embedded resource as a stream
Stream s = a.GetManifestResourceStream(
string.Format("{0}.{1}", sNamespace, sTextFile)
);
// read the contents of the stream into a string
StreamReader sr = new StreamReader(s);
String sContents = sr.ReadToEnd();
sr.Close();
s.Close();
return sContents;
}
javascript.txt嵌入资源包含了按钮在Javascript的onclick句柄中执行的客户端方法PleaseWait()。这段代码也调用了一个客户端方法HideDiv()以隐藏按钮的容器
原文链接:http://www.codeproject.com/aspnet/PleaseWaitButton.asp
Download Source Project - 7 Kb
Download Demo Project - 30 Kb
http://www.cnblogs.com/jeffamy/archive/2006/08/20/481952.html