ASP.Net AJAX Extender - List Search Box

2/29/2008

If you do web development and work with ASP.Net, most likely you've also been using their AJAX implementation and probably also their Control Toolkit. I've been using it for a while now and I have to say that most of the time I have to tweak or even reinvent one of their items because of their implementation. One such item was the ListSearchExtender. It worked perfectly well but I found that quite a few people never used the feature because they didn't pay attention to the text that popped up even if an explanation was above the list.

To mitigate that issue I found that using a text box with text above it explaining its purpose fixed that problem. Another issue that I found was that sometimes a design called for an item in a list to be indented. This can be done by adding code similar to this:

   1: ListItem IndentedItem = new ListItem();
   2: string EncodedString = "    ";
   3: System.IO.StringWriter Writer = new System.IO.StringWriter();
   4: HttpUtility.HtmlDecode(EncodedString, Writer);
   5: string DecodedString = Writer.ToString();
   6: IndentedItem.Text = DecodedString + DecodedString+"Item Name";

And adding that to the list. This however makes the item completely unsearchable as the ListSearchExtender does not take into account HTML encoding nor are you capable of specifying characters to ignore from what I can tell. This also meant that I had to develop my own version of that item. And to help others so that they don't have to reinvent the wheel (although they may still want to tweak this code as there is little optimization done), I'm giving you the code:

FilteredSearchBoxExtender.cs

   1: using System.ComponentModel;
   2: using System.Web.UI;
   3: using System.Web.UI.WebControls;
   4: using AjaxControlToolkit; 
   5:  
   6: [assembly: System.Web.UI.WebResource("AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.js", "text/javascript")] 
   7:  
   8: namespace AJAXControls.FilteredSearchBox
   9: {
  10:     [Designer(typeof(FilteredSearchBoxDesigner))]
  11:     [ClientScriptResource("AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior", "AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.js")]
  12:     [TargetControlType(typeof(Control))]
  13:     public class FilteredSearchBoxExtender : ExtenderControlBase
  14:     {
  15:         /// <summary>
  16:         /// The id of the control to update
  17:         /// </summary>
  18:         [ExtenderControlProperty()]
  19:         [RequiredProperty()]
  20:         [IDReferenceProperty(typeof(TextBox))]
  21:         public string SearchBoxID
  22:         {
  23:             get
  24:             {
  25:                 return GetPropertyValue("SearchBoxID", "");
  26:             }
  27:             set
  28:             {
  29:                 SetPropertyValue("SearchBoxID", value);
  30:             }
  31:         }
  32:     }
  33: }

FilteredSearchBoxBehavior.js

   1: Type.registerNamespace('AJAXControls.FilteredSearchBox'); 
   2:  
   3: AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior = function(element) {
   4:     AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.initializeBase(this, [element]); 
   5:  
   6:     // TODO : (Step 1) Add your property variables here
   7:     this._SearchBoxIDValue=null;
   8:     this._CurrentPosition=0;
   9: }
  10: AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.prototype = {
  11:     initialize : function() {
  12:         AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.callBaseMethod(this, 'initialize'); 
  13:  
  14:         // TODO: Add your initalization code here
  15:         Sys.UI.DomEvent.addHandler($get(this._SearchBoxIDValue), 'keyup',Function.createDelegate(this, this._onkeyup));
  16:         Sys.UI.DomEvent.addHandler($get(this._SearchBoxIDValue), 'keydown',Function.createDelegate(this, this._onkeydown));
  17:     },
  18:     
  19:     _onkeyup : function(e) {
  20:         var SearchText = $get(this._SearchBoxIDValue).value;
  21:         var NumKeys = $get(this._SearchBoxIDValue).value.length;
  22:         
  23:         var Text = SearchText ? SearchText.toLowerCase() : "";
  24:         
  25:         if(Text.length == 0)
  26:         {
  27:             this.get_element().selectedIndex = 0;
  28:             this._CurrentPosition=0;
  29:             return;
  30:         }
  31:         else
  32:         {
  33:             for(var i = this._CurrentPosition; i <= this.get_element().options.length-1; i++)
  34:             {
  35:                 var Item=this.get_element()[i].text.toLowerCase();
  36:                 while(escape(Item).indexOf("%A0")==0)                //This is where we remove all of the leading spaces
  37:                 {
  38:                     Item=Item.slice(1);
  39:                 }
  40:                 if(Item.startsWith(Text))
  41:                 {
  42:                     this.get_element().selectedIndex=i;
  43:                     this._CurrentPosition=i;
  44:                     return;
  45:                 }
  46:             }
  47:         }
  48:     },
  49:     
  50:     _onkeydown : function(e) {
  51:         if(e.keyCode == Sys.UI.Key.backspace)
  52:         {
  53:             this._CurrentPosition=0;
  54:         }
  55:     }, 
  56:  
  57:     dispose : function() {
  58:         // TODO: Add your cleanup code here 
  59:  
  60:         AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.callBaseMethod(this, 'dispose');
  61:     }, 
  62:  
  63:     // TODO: (Step 2) Add your property accessors here
  64:     get_SearchBoxID : function() {
  65:         return this._SearchBoxIDValue;
  66:     }, 
  67:  
  68:     set_SearchBoxID : function(value) {
  69:         this._SearchBoxIDValue = value;
  70:     }
  71: }
  72: AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior.registerClass('AJAXControls.FilteredSearchBox.FilteredSearchBoxBehavior', AjaxControlToolkit.BehaviorBase); 

You might want to note that the extender was created in a seperate project for my various AJAX extenders called AJAXControls and also within its own directory called FilteredSearchBox. So you might have to change some stuff around to use this. Also the designer file was left blank. So when you create the extender you can pretty much ignore that portion. Anyway, I have quite a few other extenders that I've created and as far as I'm aware they should all work with 2.0 as well as 3.5, so expect every once in a while to pepper the site with these from time to time.



Comments