分享
 
 
 

ASP.NET服务器控件PleaseWaitButton[翻译]

王朝asp·作者佚名  2008-05-30
窄屏简体版  字體: |||超大  

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动画图像),如果存在,当按钮被点击它将取代按钮。这个属性将变成<img>标记中的src属性。

PleaseWaitType

PleaseWaitTypeEnum枚举值之一:TextOnly,ImageOnly,TextThenImage,或者ImageThenText。它控制消息和图片的布局。

下面是一个.aspx文件示例,它演示了一个设置了PleaseWaitText和PleaseWaitImage的PleastWaitButton。

<%@ Page language="C#" %>

<%@ Register TagPrefix="cc1" Namespace="JavaScriptControls"

Assembly="PleaseWaitButton" %>

<script runat="server">

private void PleaseWaitButton1_Click(object sender, System.EventArgs e)

{

// Server-side Click event handler;

// simulate something that could take a long time,

// like a file upload or time-consuming server processing

DateTime dt = DateTime.Now.AddSeconds(5);

while (DateTime.Now < dt)

{

// do nothing; simulate a 5-second pause

}

// at the end of the loop display a success message

// and hide the submit form

panelSuccess.Visible = true;

PleaseWaitButton1.Visible = false;

}

</script>

<html>

<head>

<title>Testing PleaseWaitButton</title>

</head>

<body>

<form id="Form1" method="post" runat="server">

<P>Testing the PleaseWaitButton control.</p>

<cc1:PleaseWaitButton id="PleaseWaitButton1" runat="server"

Text="Click me to start a time-consuming process"

PleaseWaitText="Please Wait "

PleaseWaitImage="pleaseWait.gif"

OnClick="PleaseWaitButton1_Click" />

<asp:Panel id="panelSuccess" runat="server"

visible="false">

Thank you for submitting this form. You are truly

the coolest user I've ever had the pleasure of serving.

No, really, I mean it. There have been others, sure,

but you are really in a class by yourself.

</asp:Panel>

</form>

</body>

</html>

How It Works

PleaseWaitButton控件在<div>标记中呈现了一个标准的asp.net Button。它也呈现了一个空的<div>标记给

信息/图像。在点击按钮时,由Javascript函数(见下面的客户端函数)控制按钮的隐藏和信息的显示。为了方便起见,由PleaseWaitButton服务器控件处理所有必需的javascript客户端代码的实施。

由于PleaseWaitButton实施它自己的javascript onclick句柄,所以我们必须采取一些额外的措施来保持原有的onclick句柄,并且允许控件清晰地运行一些客户端验证代码。为了达到此目的,我们首先把Button基类还原为一个字符串缓冲,然后巧妙地处理它,把我们定义的onclick代码包含进去。

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 <div>

// that will be populated client-side via javascript

// with a "please wait" message"

output.Write(string.Format("<div id='pleaseWaitButtonDiv2_{0}'>",

this.ClientID));

output.Write("</div>");

// render the button in an encapsulating <div> tag of its own

output.Write(string.Format("<div id='pleaseWaitButtonDiv_{0}'>",

this.ClientID));

output.Write(sButtonHtml);

output.Write("</div>");

}

这种把button还原成一个字符串缓冲然后处理它的onclick内容的技术是一件很危险的事情(is certainly a hack). 但它可以让我们在父button类中实施标准的验证代码,然后再实现我们的PleaseWait() Javascript函数调用。如果不这样做,我们只能在验证代码之前就在onclick属性中实施我们的PleaseWait()函数调用,除非我们愿意完全重写父Button类的属性的呈现。这样就算页面上有输入错误也会产生我们并不希望的按钮隐藏和显示"please wait"信息的效果。因此,我们必须在onclick句柄中强行令我们的客户端PleaseWait()函数出现在客户端页面验证之后。

onclick属性的修改发生在ModifyJavaScriptOnClick()函数中。这个函数获取按钮呈现的HTML字符串,并检查看是否存在onclick属性。如果是,这个函数会检查是否有使用客户端验证代码。如果是这种情况的话,我们定义的PleaseWait()函数会加在已经存在的onclick代码的最后面,紧跟在客户端检查的boolin变量Page_IsValid后面。这个变量代表是否使用了验证控件。如果Page_IsValid的值是false,"Please wait"信息将不显示。如果为True则显示。

private string ModifyJavaScriptOnClick(string sHtml)

{

// Thanks to CodeProject member KJELLSJ (Kjell-Sverre Jerijaervi)

// for code ideas to allow the button to work with client-side validation

string sReturn = "";

string sPleaseWaitCode = GeneratePleaseWaitJavascript();

// is there an existing onclick attribute?

Regex rOnclick = new Regex("onclick=\"(?<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(

"<img src=\"{0}\" align=\"absmiddle\" alt=\"{1}\"/>"

, _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("\"", "&quot;");

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("<script language='JavaScript'>");

sb.Append("if (document.images) { ");

sb.AppendFormat("{0} = new Image();", sImgName);

sb.AppendFormat("{0}.src = \"{1}\";", sImgName, sImage);

sb.Append(" } ");

sb.Append("</script>");

this.Page.RegisterClientScriptBlock(sImgName + "_PreloadScript",

sb.ToString());

}

Client-side functions

嵌入的文本文件javascript.txt包含了隐藏按钮的<div>和显示"please wait"信息或图像的客户端代码。这些代码在重写的OnInit()方法中调用的私有方法RegisterJavascriptFromResource()加载。这个方法调用泛型方法GetEmbeddedTextFile() ,在这个泛型方法中把文件做为源加载而把内容返回成字符串。

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()以隐藏按钮的容器<div>,然后通过设置innerHTML属性把信息或图像组装进之前空的<div>标记中。辅助函数GetDiv()则是通过检查document.getElementById, document.all, 和 document.layers用id返回一个<div>对象,保证了不同浏览器的兼容性。下面是javascript.txt的全部代码:

<script language="JavaScript">

function GetDiv(sDiv)

{

var div;

if (document.getElementById)

div = document.getElementById(sDiv);

else if (document.all)

div = eval("window." + sDiv);

else if (document.layers)

div = document.layers[sDiv];

else

div = null;

return div;

}

function HideDiv(sDiv)

{

d = GetDiv(sDiv);

if (d)

{

if (document.layers) d.visibility = "hide";

else d.style.visibility = "hidden";

}

}

function PleaseWait(sDivButton, sDivMessage, sInnerHtml)

{

HideDiv(sDivButton);

var d = GetDiv(sDivMessage);

if (d) d.innerHTML = sInnerHtml;

}

</script>

原文链接: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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有