PleaseWaitButton ASP.NET Server Control

http://www.codeproject.com/aspnet/PleaseWaitButton.asp

 

Introduction

It is often useful upon a form submission in a web application to display a "please wait" message, or animated .gif image, particularly if the submission process lasts a few seconds or more. I recently developed a survey submission application in which internal users upload excel spreadsheets through a web page. The application inserts the uploaded data from the spreadsheets into a database. The upload/insert process may only take a few seconds, but even a few seconds on the web is a noticeable wait. When testing, some users clicked the upload button repeatedly; it was useful to provide a visual clue that the upload was underway. For that matter, it was useful to hide the upload button altogether, preventing multiple clicks. The control presented here, a subclass of the Button control, demonstrates how client-side JavaScript code encapsulated in an ASP.NET server control can provide such functionality conveniently.

Though there are lots of JavaScript examples out there to accomplish this type of thing, I came across some issues when attempting to encapsulate this functionality in an ASP.NET control. My initial attempts involved disabling the button with a JavaScript onClick handler and substituting different text, but I found this would interfere with the functioning of the ASP.NET server-side Click event. What ultimately worked, providing better cross-browser support as well, was to have the button rendered within a <div> tag. This <div> can then be hidden without interfering with the ASP.NET Click event.

Using the control

As its descendent, the PleaseWaitButton functions just like a regular Button control. It exposes three additional properties to govern the display of the "Please Wait" message or image once the button is clicked.

  • PleaseWaitText
    This is the client-side text message to display, if any, in place of the button when clicked.
  • PleaseWaitImage
    This is the image file (typically an animated .gif) to display, if any, in place of the button when clicked. This property serves as the src attribute for the resulting <img> tag.
  • PleaseWaitType
    One of the PleaseWaitTypeEnum values – TextOnly, ImageOnly, TextThenImage, or ImageThenText – which governs the layout of the message and/or image.

Here is an example .aspx file demonstrating a PleaseWaitButton with both PleaseWaitText and PleaseWaitImage set:

<%@ 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

The PleaseWaitButton control renders a standard Button within a <div> tag. It also renders an initially empty <div> tag for the message/image.

protected override void Render(HtmlTextWriter output)
{
    // 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));
    base.Render(output);
    output.Write("</div>");
}

These <div> tags are then manipulated through a client-side JavaScript onclick handler. The layout (text and/or image) and onclick assignment are made in the overridden OnPreRender method:

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    // get the current onclick handler if any, and
    // set up the text message and/or image string
    // depending on the values of PleaseWaitText and
    // PleaseWaitImage
    string sCurrent = this.Attributes["onclick"];
    string sMessage = "";
    string sText = _pleaseWaitText;
    string sImage = (_pleaseWaitImage != String.Empty 
                     ? string.Format(
                        "<img src=/"{0}/" align=/"absmiddle/" alt=/"{1}/"/>"
                        , _pleaseWaitImage, _pleaseWaitText )
                     : String.Empty);

    // If we're using an image, register some javascript
    // for client-side image preloading
    if (sImage != String.Empty 
        && _pleaseWaitType != PleaseWaitTypeEnum.TextOnly)
            RegisterJavascriptPreloadImage(_pleaseWaitImage);
        
    // 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;
    }

    // Add the final code chunk as the javascript onclick handler
    string sCode 
        = string.Format(
    "PleaseWait('pleaseWaitButtonDiv_{0}', 'pleaseWaitButtonDiv2_{1}', '{2}');"
                      , this.ClientID, this.ClientID, sMessage);
    
    this.Attributes["onclick"] = sCode + sCurrent;
}

If a PleaseWaitImage is specified, the private method RegisterJavascriptPreloadImage() is executed. This outputs client-side code to preload the image. The registration key is based on the image name; if multiple buttons are used on a page with the same image, the preload script is rendered only once for the image.

private void RegisterJavascriptPreloadImage(string sImage)
{
    // javascript variable name for the image
    string sImgName = "img_" 
     + sImage.Replace(".","_").Replace("+", "_").Replace(" ", "_");

    // construct the client-side script to preload
    // the image
    StringBuilder sb = new StringBuilder();
    sb.Append("<script language='JavaScript'>");
    sb.AppendFormat("{0} = new Image();", sImgName);
    sb.AppendFormat("{0}.src = /"{1}/";", sImgName, sImage);
    sb.Append("</script>");

    // register the script with the page
    this.Page.RegisterClientScriptBlock(
        sImgName + "_PreloadScript", sb.ToString());
}

The embedded text file javascript.txt contains the client-side code to hide the button's <div> and display the "please wait" message/image. This code is loaded in the overridden OnInit() method with a call to the private method RegisterJavascriptFromResource(). This method calls the more generic method GetEmbeddedTextFile() which loads a text file embedded as a resource and returns the contents as a string.

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;
}

The javascript.txt embedded resource contains the client-side method PleaseWait() which is executed in the JavaScript onclick handler for the button. This code calls the client method HideDiv() to hide the button's containing <div>, then populates the previously empty <div> tag with the message/image by setting its innerHTML property. The helper function GetDiv(), attempting to maintain cross-browser compatibility, inspects document.getElementById, document.all, and document.layers to return a <div> object given its id. The complete client-side code in javascript.txt follows:

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

Summary

The ASP.NET server control PleaseWaitButton presented here renders a standard Button within <div> tags and companion client-side JavaScript to present users with a "please wait" message or image. Such a message can provide users with a useful visual cue for time-consuming form processing and prevent accidental multiple clicks.

About Mike Ellison


I work for the University of Nevada, Las Vegas in the Office of Institutional Analysis and Planning. Our office is charged with the mission of deriving useful information from institutional data. Within the context of that mission, my office mates and I apply technology in the form of custom data processing applications, data extraction and analysis tools, reporting tools, relational databases, OLAP solutions, data warehousing, and data mining.

Click here to view Mike Ellison's online profile.


Other popular ASP.NET articles:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值