Progress Bar AJAX Extender for ASP.Net

8/22/2008

If you've looked for AJAXified progress bars, you've probably seen Matt Berseth's version here. It works great but there were simply certain things I couldn't do with it. For instance, I wanted to know where I was in long async postbacks, have a vertical progress bar, and have the percentage and progress bar in vastly different locations. As such I decided to go ahead and create my own version. Definitely not perfect by any stretch of the imagination, but it does do what I wanted it to.

ProgressBarExtender.cs

   1: using System;
   2: using System.Web.UI.WebControls;
   3: using System.Web.UI;
   4: using System.ComponentModel;
   5: using System.ComponentModel.Design;
   6: using AjaxControlToolkit;
   7:  
   8: [assembly: System.Web.UI.WebResource("AJAXControls.ProgressBar.ProgressBarBehavior.js", "text/javascript")]
   9:  
  10: namespace AJAXControls.ProgressBar
  11: {
  12:     [Designer(typeof(ProgressBarDesigner))]
  13:     [RequiredScript(typeof(CommonToolkitScripts))]
  14:     [RequiredScript(typeof(AnimationScripts))]
  15:     [ClientScriptResource("AJAXControls.ProgressBar.ProgressBarBehavior", "AJAXControls.ProgressBar.ProgressBarBehavior.js")]
  16:     [TargetControlType(typeof(Control))]
  17:     public class ProgressBarExtender : ExtenderControlBase
  18:     {
  19:         // TODO: Add your property accessors here.
  20:         //
  21:         [ExtenderControlProperty]
  22:         [DefaultValue(0)]
  23:         public int MaxSize
  24:         {
  25:             get
  26:             {
  27:                 return GetPropertyValue("MaxSize", 0);
  28:             }
  29:             set
  30:             {
  31:                 SetPropertyValue("MaxSize", value);
  32:             }
  33:         }
  34:  
  35:         /// <summary>
  36:         /// The container control id
  37:         /// </summary>
  38:         [ExtenderControlProperty()]
  39:         [IDReferenceProperty(typeof(WebControl))]
  40:         public string PercentageControlID
  41:         {
  42:             get
  43:             {
  44:                 return GetPropertyValue("PercentageControlID", "");
  45:             }
  46:             set
  47:             {
  48:                 SetPropertyValue("PercentageControlID", value);
  49:             }
  50:         }
  51:  
  52:         [ExtenderControlProperty]
  53:         [DefaultValue(0)]
  54:         public int MinSize
  55:         {
  56:             get
  57:             {
  58:                 return GetPropertyValue("MinSize", 0);
  59:             }
  60:             set
  61:             {
  62:                 SetPropertyValue("MinSize", value);
  63:             }
  64:         }
  65:  
  66:         [ExtenderControlProperty]
  67:         [DefaultValue(100)]
  68:         public int Timer
  69:         {
  70:             get
  71:             {
  72:                 return GetPropertyValue("Timer", 0);
  73:             }
  74:             set
  75:             {
  76:                 SetPropertyValue("Timer", value);
  77:             }
  78:         }
  79:  
  80:         /// <summary>
  81:         /// Either width or height
  82:         /// </summary>
  83:         [ExtenderControlProperty()]
  84:         [RequiredProperty()]
  85:         public string Mode
  86:         {
  87:             get
  88:             {
  89:                 return GetPropertyValue("Mode", "");
  90:             }
  91:             set
  92:             {
  93:                 SetPropertyValue("Mode", value);
  94:             }
  95:         }
  96:  
  97:         /// <summary>
  98:         /// The name of the function to call
  99:         /// </summary>
 100:         [ExtenderControlProperty()]
 101:         [RequiredProperty()]
 102:         public string ScriptName
 103:         {
 104:             get
 105:             {
 106:                 return GetPropertyValue("ScriptName", "");
 107:             }
 108:             set
 109:             {
 110:                 SetPropertyValue("ScriptName", value);
 111:             }
 112:         }
 113:  
 114:         /// <summary>
 115:         /// The name of the page on which to call the function
 116:         /// </summary>
 117:         [ExtenderControlProperty()]
 118:         [RequiredProperty()]
 119:         public string ScriptLocation
 120:         {
 121:             get
 122:             {
 123:                 return GetPropertyValue("ScriptLocation", "");
 124:             }
 125:             set
 126:             {
 127:                 SetPropertyValue("ScriptLocation", value);
 128:             }
 129:         }
 130:     }
 131: }

ProgressBarBehavior.js

   1: // README
   2: //
   3: // There are two steps to adding a property:
   4: //
   5: // 1. Create a member variable to store your property
   6: // 2. Add the get_ and set_ accessors for your property
   7: //
   8: // Remember that both are case sensitive!
   9:  
  10:  
  11: /// <reference name="MicrosoftAjaxTimer.debug.js" />
  12: /// <reference name="MicrosoftAjaxWebForms.debug.js" />
  13: /// <reference name="AjaxControlToolkit.ExtenderBase.BaseScripts.js" assembly="AjaxControlToolkit" />
  14:  
  15:  
  16: Type.registerNamespace('AJAXControls.ProgressBar');
  17:  
  18: AJAXControls.ProgressBar.ProgressBarBehavior = function(element) {
  19:     AJAXControls.ProgressBar.ProgressBarBehavior.initializeBase(this, [element]);
  20:  
  21:     // TODO : (Step 1) Add your property variables here
  22:     this._MaxSizeValue = 0;
  23:     this._MinSizeValue = 0;
  24:     this._TotalDistance = 0;
  25:     this._TimerValue = 0.1;
  26:     this._Animation = null;
  27:     this._ScriptLocationValue = '';
  28:     this._ScriptNameValue = '';
  29:     this._Counter = 0;
  30:     this._TimeOutValue = 2000;
  31:     this._CurrentValue = 0;
  32:     this._PercentageControlIDValue = null;
  33:     this._ModeValue = 'width';
  34: }
  35: AJAXControls.ProgressBar.ProgressBarBehavior.prototype = {
  36:     initialize: function() {
  37:         AJAXControls.ProgressBar.ProgressBarBehavior.callBaseMethod(this, 'initialize');
  38:  
  39:         // TODO: Add your initalization code here
  40:         this._TotalDistance = this._MaxSizeValue - this._MinSizeValue;
  41:         this._Animation = new $AA.LengthAnimation(this.get_element(), this._TimerValue, 50, 'style', this._ModeValue, 0, 0, '%');
  42:         this._Animation.add_ended(Function.createDelegate(this, this.AnimationEnd));
  43:         this._Animation.initialize();
  44:     },
  45:  
  46:     show: function() {
  47:         this.get_element().style.display = '';
  48:         this.get_element().style.visibility = '';
  49:         this._Animation.play();
  50:     },
  51:  
  52:     hide: function() {
  53:         this.get_element().style.display = 'none';
  54:         this.get_element().style.visibility = 'hidden';
  55:         this._Animation.stop();
  56:     },
  57:  
  58:     AnimationEnd: function() {
  59:         if (this._CurrentValue < 100) {
  60:             ++this._Counter;
  61:             Sys.Net.WebServiceProxy.invoke(this._ScriptLocationValue, this._ScriptNameValue, false, {}, Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError), this._Counter, this._TimeOutValue);
  62:         }
  63:     },
  64:     _onMethodComplete: function(result, userContext, methodName) {
  65:         if (this._Counter == userContext) {
  66:             if (result > 100)
  67:                 result = 100;
  68:             this._StartValue = this._CurrentValue;
  69:             this._EndValue = result;
  70:             this._CurrentValue = result;
  71:             if (this._PercentageControlIDValue) {
  72:                 var PercentHolder = $get(this._PercentageControlIDValue);
  73:                 PercentHolder.innerHTML = result + "% completed";
  74:             }
  75:             this._Animation.set_startValue(this._StartValue);
  76:             this._Animation.set_endValue(this._EndValue);
  77:             this._Animation.play();
  78:         }
  79:     },
  80:     _onMethodError: function(webServiceError, userContext, methodName) {
  81:         if (webServiceError.get_timedOut()) {
  82:             this.get_element().innerHTML = AjaxControlToolkit.Resources.DynamicPopulate_WebServiceTimeout;
  83:         }
  84:         else {
  85:             this.get_element().innerHTML = String.format(AjaxControlToolkit.Resources.DynamicPopulate_WebServiceError, webServiceError.get_statusCode());
  86:             this.get_element().innerHTML += this._ScriptLocationValue;
  87:             this.get_element().innerHTML += this._ScriptNameValue;
  88:         }
  89:     },
  90:  
  91:     dispose: function() {
  92:         // TODO: Add your cleanup code here
  93:  
  94:         AJAXControls.ProgressBar.ProgressBarBehavior.callBaseMethod(this, 'dispose');
  95:     },
  96:  
  97:     // TODO: (Step 2) Add your property accessors here
  98:     get_MaxSize: function() {
  99:         return this._MaxSizeValue;
 100:     },
 101:  
 102:     set_MaxSize: function(value) {
 103:         this._MaxSizeValue = value;
 104:     },
 105:     get_MinSize: function() {
 106:         return this._MinSizeValue;
 107:     },
 108:  
 109:     set_MinSize: function(value) {
 110:         this._MinSizeValue = value;
 111:     },
 112:     get_Timer: function() {
 113:         return this._TimerValue;
 114:     },
 115:  
 116:     set_Timer: function(value) {
 117:         this._TimerValue = value;
 118:     },
 119:     get_ScriptName: function() {
 120:         return this._ScriptNameValue;
 121:     },
 122:  
 123:     set_ScriptName: function(value) {
 124:         this._ScriptNameValue = value;
 125:     },
 126:     get_ScriptLocation: function() {
 127:         return this._ScriptLocationValue;
 128:     },
 129:  
 130:     set_ScriptLocation: function(value) {
 131:         this._ScriptLocationValue = value;
 132:     },
 133:     get_PercentageControlID: function() {
 134:         return this._PercentageControlIDValue;
 135:     },
 136:  
 137:     set_PercentageControlID: function(value) {
 138:         this._PercentageControlIDValue = value;
 139:     },
 140:     get_Mode: function() {
 141:         return this._ModeValue;
 142:     },
 143:  
 144:     set_Mode: function(value) {
 145:         this._ModeValue = value;
 146:     }
 147: }
 148: AJAXControls.ProgressBar.ProgressBarBehavior.registerClass('AJAXControls.ProgressBar.ProgressBarBehavior', AjaxControlToolkit.BehaviorBase);

First, if you've used Matt's code, there is no continuous/manual modes. There is a mode that you can set, but it is specifically there to specify whether you want the bar to change it's height or width (or really any CSS value that takes a percentage). The code allows you to pick an image, div, span, etc. as the target. You pick a maximum percentage, minimum percentage, animation timer value in seconds, and an optional spot to specify where to place the percentage information. None of that is too big of a change, however instead of calling the javascript code to change the value, the code automatically does a call to the server (at whatever location you specify), which returns a float value, stating what percentage of the work has been completed. Once it is equal to or greater that the maximum value, it stops calling the server.

When everything is done you end up with this (it's an image, no need to click the button):

ProgressBar

And yes, I'm so lazy that I didn't change the button's text to anything meaningful... However, I put the button there to bring up one aspect of the code that still requires some javascript. Specifically, you have to call show on the object:

   1: <script type="text/javascript">
   2:     //<![CDATA[
   3:     function onClick(sender) {
   4:         var Temp = $find('ctl00_ContentPlaceHolder1_ProgressBarExtender1');
   5:         if (Temp) {
   6:             Temp.show();
   7:         }
   8:     }
   9:     //]]>
  10: </script> 
  11:  
  12: <input id="Button1" type="button" value="button" onclick="onClick(this);" /> 

As you can see, the button simply calls the function above, which called the progress bar's show function. There is also a hide function which stops any animation and hides the target control. That's all there is to it. Anyway, I hope this helps out someone. And in the future I plan on using this in conjunction with the file upload/update panel post I made to show you how to make a file upload system similar to GMail's. For now though, try out the code, leave some feedback, and happy coding.



Comments