ASP.Net AJAX List Extender

3/31/2008

I admit that I really like the AJAX Toolkit however I'm constantly running into instances where I wish I had more leeway with how it did things. One instance was the reorderlist extender.

When using the reorderlist extender I ran into two problems on a project. The first issue was the fact that I had to bind it to data. I needed the ability to feed it information after it had been rendered and as far as I'm aware, that wasn't an option. The second issue was the fact that the information for each item in the list was large (sometimes an entire page of data). The drag and drop model in this instance simply made it infeasible, instead I needed the ability to click on buttons in order to switch positions. So once again I created my own extender...

ReorderListExtender.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.ReorderList.ReorderListBehavior.js", "text/javascript")]
   9:  
  10: namespace AJAXControls.ReorderList
  11: {
  12:     [Designer(typeof(ReorderListDesigner))]
  13:     [ClientScriptResource("AJAXControls.ReorderList.ReorderListBehavior", "AJAXControls.ReorderList.ReorderListBehavior.js")]
  14:     [TargetControlType(typeof(Control))]
  15:     public class ReorderListExtender : ExtenderControlBase
  16:     {
  17:         [ExtenderControlProperty]
  18:         [RequiredProperty()]
  19:         public string ListItems
  20:         {
  21:             get
  22:             {
  23:                 return GetPropertyValue("ListItems", "");
  24:             }
  25:             set
  26:             {
  27:                 SetPropertyValue("ListItems", value);
  28:             }
  29:         }
  30:  
  31:         [ExtenderControlProperty]
  32:         public string ListOrder
  33:         {
  34:             get
  35:             {
  36:                 return GetPropertyValue("ListOrder", "");
  37:             }
  38:             set
  39:             {
  40:                 SetPropertyValue("ListOrder", value);
  41:             }
  42:         }
  43:  
  44:         [ExtenderControlProperty]
  45:         [RequiredProperty()]
  46:         [IDReferenceProperty(typeof(System.Web.UI.HtmlControls.HtmlInputButton))]
  47:         public string SaveButtonID
  48:         {
  49:             get
  50:             {
  51:                 return GetPropertyValue("SaveButtonID", "");
  52:             }
  53:             set
  54:             {
  55:                 SetPropertyValue("SaveButtonID", value);
  56:             }
  57:         }
  58:  
  59:         /// <summary>
  60:         /// The name of the function to call
  61:         /// </summary>
  62:         [ExtenderControlProperty()]
  63:         [RequiredProperty()]
  64:         public string SaveScriptName
  65:         {
  66:             get
  67:             {
  68:                 return GetPropertyValue("SaveScriptName", "");
  69:             }
  70:             set
  71:             {
  72:                 SetPropertyValue("SaveScriptName", value);
  73:             }
  74:         }
  75:  
  76:         /// <summary>
  77:         /// The name of the page on which to call the function
  78:         /// </summary>
  79:         [ExtenderControlProperty()]
  80:         [RequiredProperty()]
  81:         public string ScriptLocation
  82:         {
  83:             get
  84:             {
  85:                 return GetPropertyValue("ScriptLocation", "");
  86:             }
  87:             set
  88:             {
  89:                 SetPropertyValue("ScriptLocation", value);
  90:             }
  91:         }
  92:  
  93:         /// <summary>
  94:         /// The name of the up arrow image
  95:         /// </summary>
  96:         [ExtenderControlProperty()]
  97:         [RequiredProperty()]
  98:         public string UpArrowImage
  99:         {
 100:             get
 101:             {
 102:                 return GetPropertyValue("UpArrowImage", "");
 103:             }
 104:             set
 105:             {
 106:                 SetPropertyValue("UpArrowImage", value);
 107:             }
 108:         }
 109:  
 110:         /// <summary>
 111:         /// The name of the down arrow image
 112:         /// </summary>
 113:         [ExtenderControlProperty()]
 114:         [RequiredProperty()]
 115:         public string DownArrowImage
 116:         {
 117:             get
 118:             {
 119:                 return GetPropertyValue("DownArrowImage", "");
 120:             }
 121:             set
 122:             {
 123:                 SetPropertyValue("DownArrowImage", value);
 124:             }
 125:         }
 126:     }
 127: }

ReorderListBehavior.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="MicrosoftAjax.debug.js" />
  12: /// <reference name="MicrosoftAjaxTimer.debug.js" />
  13: /// <reference name="MicrosoftAjaxWebForms.debug.js" />
  14: /// <reference name="AjaxControlToolkit.ExtenderBase.BaseScripts.js" assembly="AjaxControlToolkit" />
  15: /// <reference path="AjaxControlToolkit.Compat.DragDrop.DragDropScripts.js" />
  16:  
  17:  
  18: Type.registerNamespace('AJAXControls.ReorderList');
  19:  
  20: AJAXControls.ReorderList.ReorderListBehavior = function(element) {
  21:     AJAXControls.ReorderList.ReorderListBehavior.initializeBase(this, [element]);
  22:  
  23:     // TODO : (Step 1) Add your property variables here
  24:     this._ListItems=[];
  25:     this._ListOrder=[];
  26:     
  27:     this._parentNode=null;
  28:     this._HolderDiv=null;
  29:     this._SaveButtonID=null;
  30:     this._Counter=0;
  31:     this._ScriptNameValue=null;
  32:     this._ScriptLocationValue=null;
  33:     this._DeleteScriptName=null;
  34:     this._IndentInsertItem=false;
  35:     this._DownArrowImageValue=null;
  36:     this._UpArrowImageValue=null;
  37: }
  38: AJAXControls.ReorderList.ReorderListBehavior.prototype = {
  39:     initialize : function() {
  40:         AJAXControls.ReorderList.ReorderListBehavior.callBaseMethod(this, 'initialize');
  41:  
  42:         this._HolderDiv=document.createElement("div");
  43:         this._parentNode=$get(this._ListItems[0]).parentNode;
  44:         // TODO: Add your initalization code here
  45:         
  46:         for(var x=0;x<this._ListItems.length;++x)
  47:         {
  48:             //$get(this._ListItems[x]).style.display="inline";
  49:             var UpArrow=document.createElement("img");
  50:             UpArrow.id="UpArrow_"+x;
  51:             UpArrow.src=this._UpArrowImageValue;
  52:             $addHandler(UpArrow,"click",Function.createDelegate(this,this.OnClick));
  53:             $get(this._ListItems[x]).insertBefore(UpArrow,$get(this._ListItems[x]).firstChild);
  54:             
  55:             //HolderDiv.appendChild(UpArrow);
  56:             var DownArrow=document.createElement("img");
  57:             DownArrow.id="DownArrow_"+x;
  58:             DownArrow.src=this._DownArrowImageValue;
  59:             $addHandler(DownArrow,"click",Function.createDelegate(this,this.OnClick));
  60:             $get(this._ListItems[x]).insertBefore(DownArrow,$get(this._ListItems[x]).firstChild);
  61:             
  62:             //HolderDiv.appendChild(DownArrow);
  63:             this._HolderDiv.appendChild($get(this._ListItems[x]));
  64:             //HolderDiv.appendChild(document.createElement("br"));
  65:         }
  66:         
  67:         this._parentNode.appendChild(this._HolderDiv);
  68:         
  69:         $addHandler($get(this._SaveButtonID),"click",Function.createDelegate(this,this.SaveOnClick));
  70:     },
  71:  
  72:     dispose : function() {
  73:         // TODO: Add your cleanup code here
  74:         AJAXControls.ReorderList.ReorderListBehavior.callBaseMethod(this, 'dispose');
  75:     },
  76:     
  77:     SaveOnClick:function(EventElement)
  78:     {
  79:         Sys.Net.WebServiceProxy.invoke(this._ScriptLocationValue,this._ScriptNameValue,false,{"contextKey":this.get_ListOrder()},Function.createDelegate(this, this._onMethodComplete),Function.createDelegate(this, this._onMethodError),this._Counter,1000);
  80:     },
  81:     
  82:     _onMethodComplete : function (result, userContext, methodName) {
  83:     },
  84:     
  85:     _onMethodError : function(webServiceError, userContext, methodName) {
  86:     },
  87:     
  88:     OnClick:function(EventElement)
  89:     {
  90:         var TempListItem=EventElement.target.parentNode;
  91:         if(EventElement.target.id.startsWith("UpArrow"))
  92:         {
  93:             for(var x=1;x<this._ListItems.length;x++)
  94:             {
  95:                 if(TempListItem.id==this._ListItems[x])
  96:                 {
  97:                     var NameHolder=this._ListItems[x];
  98:                     this._ListItems[x]=this._ListItems[x-1];
  99:                     this._ListItems[x-1]=NameHolder;
 100:                     var PositionHolder=this._ListOrder[x];
 101:                     this._ListOrder[x]=this._ListOrder[x-1];
 102:                     this._ListOrder[x-1]=PositionHolder;
 103:                     this._HolderDiv.insertBefore($get(NameHolder),$get(this._ListItems[x]));
 104:                     break;
 105:                 }
 106:             }
 107:         }
 108:         else if(EventElement.target.id.startsWith("DownArrow"))
 109:         {
 110:             for(var x=0;x<this._ListItems.length-1;x++)
 111:             {
 112:                 if(TempListItem.id==this._ListItems[x])
 113:                 {
 114:                     var NameHolder=this._ListItems[x];
 115:                     this._ListItems[x]=this._ListItems[x+1];
 116:                     this._ListItems[x+1]=NameHolder;
 117:                     var PositionHolder=this._ListOrder[x];
 118:                     this._ListOrder[x]=this._ListOrder[x+1];
 119:                     this._ListOrder[x+1]=PositionHolder;
 120:                     this._HolderDiv.insertBefore($get(this._ListItems[x]),$get(NameHolder));
 121:                     break;
 122:                 }
 123:             }
 124:         }
 125:     },
 126:     
 127:     // TODO: (Step 2) Add your property accessors here
 128:     get_ListOrder : function() {
 129:         var ReturnVal="";
 130:         var Splitter="";
 131:         for(var x=0;x<this._ListOrder.length;++x)
 132:         {
 133:             ReturnVal+=Splitter+this._ListOrder[x];
 134:             Splitter=",";
 135:         }
 136:         return ReturnVal;
 137:     },
 138:  
 139:     set_ListOrder : function(value) {
 140:         this._ListOrder = value.split(",");
 141:     },
 142:     
 143:     // TODO: (Step 2) Add your property accessors here
 144:     get_ListItems : function() {
 145:         var ReturnVal="";
 146:         var Splitter="";
 147:         for(var x=0;x<this._ListItems.length;++x)
 148:         {
 149:             ReturnVal+=Splitter+this._ListItems[x];
 150:             Splitter=",";
 151:         }
 152:         return ReturnVal;
 153:     },
 154:  
 155:     set_ListItems : function(value) {
 156:         this._ListItems = value.split(",");
 157:     },
 158:     
 159:     get_SaveButtonID : function() {
 160:         return this._SaveButtonID;
 161:     },
 162:  
 163:     set_SaveButtonID : function(value) {
 164:         this._SaveButtonID=value;
 165:     },
 166:     get_ScriptLocation : function() {
 167:         return this._ScriptLocationValue;
 168:     },
 169:  
 170:     set_ScriptLocation : function(value) {
 171:         this._ScriptLocationValue = value;
 172:     },
 173:     
 174:     get_SaveScriptName : function() {
 175:         return this._ScriptNameValue;
 176:     },
 177:  
 178:     set_SaveScriptName : function(value) {
 179:         this._ScriptNameValue = value;
 180:     },
 181:     
 182:     get_DownArrowImage : function() {
 183:         return this._DownArrowImageValue;
 184:     },
 185:  
 186:     set_DownArrowImage : function(value) {
 187:         this._DownArrowImageValue = value;
 188:     },
 189:     
 190:     get_UpArrowImage : function() {
 191:         return this._UpArrowImageValue;
 192:     },
 193:  
 194:     set_UpArrowImage : function(value) {
 195:         this._UpArrowImageValue = value;
 196:     }
 197: }
 198: AJAXControls.ReorderList.ReorderListBehavior.registerClass('AJAXControls.ReorderList.ReorderListBehavior', AjaxControlToolkit.BehaviorBase);

I guess I shouldn't really call this a list extender as it doesn't use HTML lists. Instead it's a list of divs or spans. The extender takes a list of divs and/or spans (a single string with each item separated by a comma) and a basic ID ordering for the items (also separated by a comma), inserts up and down arrows before the first child of the various divs/spans (the locations to the images you provide), and when you click on the arrows it switches the order of the items. In order to save the items it also requires you to associate a button with it. When the button is pressed it calls a static function (the location and name of which you specify) like this:

   1: [System.Web.Services.WebMethod]
   2: [System.Web.Script.Services.ScriptMethod]
   3: public static string SaveOrder(string contextKey)

Note that it has to be a static function, with WebMethod and ScriptMethod definitions as well as have the contextKey variable in there. The function name though can be changed by you. Also if you use it you may want to go in and edit the onMethodComplete and onMethodError functions as they don't do anything at present. Anyway, download the code and take a look, leave feedback, and happy coding.



Comments