views:

1097

answers:

4

I have a button on my webform. Clicking this button will do an HttpWebRequest during the onclick event handler. After the request we copy the response from the request into HttpContext.Current.Response and send that to the client.

This web request can take a while (up to 5 seconds, since it's generating a report). During this time the user has no indication that anything is going on, except for the browser progress bar and the spinning IE icon (if they're using IE). So I need a loading indicator while this is happening.

I've tried using javascript that fires during the button's onclick event (using OnClientClick) and while that works, I don't know how to find out when the web request is finished. Since we just send the response to the client, a full postback doesn't happen.

I've tried wrapping the button in an UpdatePanel and using the UpdateProgress, but when we send the response to HttpContext.Current.Response and call Response.End(), we get an error in the javascript, since the response isn't well formed (we're sending back an excel sheet for the user to download).

Since we're sending back a file for users to download, I don't want to pop-up a separate window, since then in IE they'd get the information bar blocking the download.

Any ideas here?

+1  A: 

A simple trick i have used in the past is to redirect to an intermediate page with an animated progress bar (gif) and then have that page do the REAL post of the data. (or even pop-up a layer with the animation on it and a polite message asking the user to wait a minute or two)

The simple feedback of the animated gif creates the illusion to the end user that the app is not stalled and they will be more patient.

Another approach is to hand the data off to a worker thread and return immediately with a message stating that the report will be emailed or made available in the "reports" section of the site when it is ready. This approach lacks the benefit of instant notification when the report is completed though.

Declan Shanaghy
The problem with redirecting the page is that you end up with the ugly information bar in IE preventing them from downloading the file.I don't want to add a whole new section to our website to pickup reports. This would likely take longer for the user anyway.
Ray
How about just popping up a layer on the current page instead of a redirect. I suggested that in my answer as an alternative to a redirect
Declan Shanaghy
How do I do this popup? In Javascript? How would I know when to close it?
Ray
+1  A: 

Here is my solution :

  1. Download and examine the samples of free Professional AJAX.NET library.
  2. Write a AjaxMethod that creates your file and returns file location as a parameter.
  3. Write your Client-Side function to call method at Step 2. When this method called show an indicator.
  4. Write a client-side callback method to hide indicator and show/download file that user requested.
  5. Add your client-side function calls yo your button element.

When your method at server-side ends your callback will be called.

Hope this helps !

Canavar
+4  A: 

As an alternative to the Professional AJAX.NET library, jQuery has a really nice way of doing this.

Take a look at this example of using a .NET PageMethod (if possible in your scenario).

You define a page method call in jQuery, you can tack on your loading... message in a hidden div.

Say what callback you want to return on success (ie when your 5 second report is generated) then hide the loading and handle the data.

Take a look at the javascript on my contact page for an example (view the source).

  1. I have a a button on the page, add the jQuery onClick.
  2. When clicked that shows a hidden loading div, makes an ajax call to a page method that takes the parameters of the form.
  3. The page method does emailing etc then returns to the form in the onSuccess javascript method I have there.
  4. The onSuccess hides the loading div.
TreeUK
A: 

The solution I'm presenting here is aimed to show a method to let a "Loading..." box to appear while you're server-side processing and to disappear when server-side processing is complete.

I'll do this with the very basic AJAX machinery (tested on FF, but IE should be ok either), i.e. not using a framework like Prototype or jQuery or Dojo, as you didn't specify your knowledge about them.

To let you better understand the trick, the following is just a small example and doesn't pretend to be an out-of-the-box solution. I tend not to be superficial, but I think a clearer example can explain better than many words.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>First Example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <style>
            .hidden {
                display: none;
            }
            .loadingInProgress {
                color: #FFFFFF;
                width: 75px;
                background-color: #FF0000;
            }
        </style>
    <script type="text/javascript">
        var httpRequest;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            httpRequest = new XMLHttpRequest();
            httpRequest.overrideMimeType('text/xml');
        } else if (window.ActiveXObject) { // IE
            try {
                httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch (e) {
                try {
                    httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
                }
                catch (e) {}
            }
        }

        if (!httpRequest) {
            alert('Giving up :( Cannot create an XMLHTTP instance');
        }

        httpRequest.onreadystatechange = function(){
            switch (httpRequest.readyState) {
            case 1: // Loading
                document.getElementById('loading').className = "loadingInProgress";
                break;
            case 4: // Complete
                document.getElementById('loading').className = "hidden";
                if (httpRequest.status == 200) {
                    // perfect!
                } else {
                    // there was a problem with the request,
                    // for example the response may be a 404 (Not Found)
                    // or 500 (Internal Server Error) response codes
                }
                break;
            }
        };

        function go() {
            httpRequest.open('GET', document.getElementById('form1').action, true);
            httpRequest.send('');
        }
    </script>

  </head>
  <body>
      <div id="loading" class="hidden">Loading...</div>
      <form id="form1" name="form1" action="doSomething.php">
          <input type="button" value="Click to submit:" onclick="go()" />
      </form>
  </body>
</html>

As you can see, there's a <div> which holds the "Loading..." message.

The principle is to show/hide the <div> depending on the XMLHttpRequest object's readyState.

I've used the onreadystatechange handler of the XMLHttpRequest to trigger the readyState change.

The back-end php script I use (declared as the form's action) does just a sleep(5), to let the "Loading..." message appear for 5 secs.

<?php
sleep(5);
header('Cache-Control: no-cache');
echo "OK";
?>

The Cache-control: no-cache header is necessary, since usually if you don't set it the browser will cache the response avoiding to resubmit the request if you should need to.

A good source for "getting started" AJAX documentation is Mozilla MDC.

The whole thing could be much more gently handled by a Javascript framework like Prototype, taking advantage of its browser-safe approach, saving you hours of debug.


Edit:

I chose php 'cause I don't know ASP.NET nor ASP, sorry about that.


Scarlet