Adding Images to a List Box using Server Controls

9/9/2008

The title for this post is a bit misleading. You can't actually add an image to a list box (well not reliably across all browsers anyway)... However you can create a faux list box that can contain images. I'm sure you've seen this a number of times before and the concept for this one is the same as all the others: create a div of a specific height/width, set overflow to auto/scroll, put a table inside it, add images/text inside the table, and add some javascript to keep track of what's currently selected. And once again, since I hate to write anything more than once if I don't have to, I created a server control to accomplish the task:

ImageListBox.cs

   1: using System;
   2: using System.Collections.Generic;
   3: using System.ComponentModel;
   4: using System.Linq;
   5: using System.Text;
   6: using System.Web;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9:  
  10: namespace Controls.ImageListBox
  11: {
  12:     [DefaultProperty("Text")]
  13:     public class ImageListItem
  14:     {
  15:         private string _Text;
  16:         private string _Value;
  17:         private bool _Selected;
  18:  
  19:         public string Text
  20:         {
  21:             get{return _Text;}
  22:             set{_Text=value;}
  23:         }
  24:  
  25:         public string Value
  26:         {
  27:             get{return _Value;}
  28:             set{_Value=value;}
  29:         }
  30:  
  31:         public bool Selected
  32:         {
  33:             get{return _Selected;}
  34:             set{_Selected=value;}
  35:         }
  36:     }
  37:  
  38:     [ParseChildren(true)]
  39:     [PersistChildren(false)]
  40:     [ToolboxData("<{0}:ImageListBox runat=server></{0}:ImageListBox>")]
  41:     public class ImageListBox : WebControl
  42:     {
  43:         private List<ImageListItem> _Items=new List<ImageListItem>();
  44:  
  45:         [NotifyParentProperty(true)]
  46:         [PersistenceMode(PersistenceMode.InnerProperty)]
  47:         [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  48:         public List<ImageListItem> Items
  49:         {
  50:             get{return _Items;}
  51:         }
  52:  
  53:         protected override void RenderContents(HtmlTextWriter output)
  54:         {
  55:             StringBuilder OutputString = new StringBuilder();
  56:             OutputString.Append("<DIV style=\"overflow:scroll;overflow-y:scroll;\" class=\"ImageListBox\"><TABLE width=\"100%\" class=\"ImageListBoxTable\"><TBODY class=\"ImageListBoxBody\">");
  57:             string SelectedValue="";
  58:             string Seperator="";
  59:             foreach (ImageListItem Item in _Items)
  60:             {
  61:                 OutputString.Append("<tr><td class=\"");
  62:                 if (Item.Selected)
  63:                 {
  64:                     OutputString.Append("ImageListItemSelected");
  65:                 }
  66:                 else
  67:                 {
  68:                     OutputString.Append("ImageListItem");
  69:                 }
  70:                 OutputString.Append("\" onclick=\"Selected("+Item.Value+",this);\">");
  71:                 OutputString.Append(Item.Text+"</td></tr>");
  72:                 if(Item.Selected)
  73:                 {
  74:                     SelectedValue+=Seperator+Item.Value;
  75:                     Seperator=";";
  76:                 }
  77:             }
  78:             OutputString.Append("</TBODY></TABLE></DIV>");
  79:             OutputString.Append("<input type=\"hidden\" id=\"ImageListItemSelectedValue\" name=\"ImageListItemSelectedValue\" value=\"" + SelectedValue + "\" />");
  80:             output.Write(OutputString.ToString());
  81:         }
  82:  
  83:         protected override void OnPreRender(EventArgs e)
  84:         {
  85:             string ScriptUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Controls.ImageListBox.ImageListBox.js");
  86:             Page.ClientScript.RegisterClientScriptInclude("ImageListBox", ScriptUrl);
  87:             base.OnPreRender(e);
  88:         }
  89:  
  90:         protected override void OnInit(EventArgs e)
  91:         {
  92:             if (Page.IsPostBack)
  93:             {
  94:                 string []Splitter={";"};
  95:                 string[] SelectedValues = null;
  96:                 if (Page.Request.HttpMethod.Equals("get", StringComparison.CurrentCultureIgnoreCase))
  97:                 {
  98:                     SelectedValues = Page.Request.QueryString["ImageListItemSelectedValue"].Split(Splitter, StringSplitOptions.RemoveEmptyEntries);
  99:                 }
 100:                 else if (Page.Request.HttpMethod.Equals("post", StringComparison.CurrentCultureIgnoreCase))
 101:                 {
 102:                     SelectedValues = Page.Request.Form["ImageListItemSelectedValue"].Split(Splitter, StringSplitOptions.RemoveEmptyEntries);
 103:                 }
 104:                 else
 105:                 {
 106:                     base.OnInit(e);
 107:                     return;
 108:                 }
 109:                 foreach (ImageListItem Item in _Items)
 110:                 {
 111:                     Item.Selected = false;
 112:                     foreach (string SelectedValue in SelectedValues)
 113:                     {
 114:                         if (Page.Request.HttpMethod.Equals("get", StringComparison.CurrentCultureIgnoreCase) && Item.Value.Equals(SelectedValue))
 115:                         {
 116:                             Item.Selected = true;
 117:                             break;
 118:                         }
 119:                         else if (Page.Request.HttpMethod.Equals("post", StringComparison.CurrentCultureIgnoreCase) && Item.Value.Equals(SelectedValue))
 120:                         {
 121:                             Item.Selected = true;
 122:                             break;
 123:                         }
 124:                     }
 125:                 }
 126:             }
 127:             base.OnInit(e);
 128:         }
 129:     }
 130: }

ImageListBox.js

   1:  
   2: var CurrentImageListItemElement=null;
   3:  
   4: function Selected(Value,Element) {
   5:  
   6:     if (Element == CurrentImageListItemElement || Element == null) {
   7:         return;
   8:     }
   9:     if (CurrentImageListItemElement) {
  10:         CurrentImageListItemElement.className = "ImageListItem";
  11:     }
  12:     else {
  13:         var Body = Element.parentNode.parentNode;
  14:         for (var x = 0; x < Body.childNodes.length; ++x) {
  15:             Body.childNodes[x].childNodes[0].className = "ImageListItem";
  16:         }
  17:     }
  18:     CurrentImageListItemElement = Element;
  19:     Element.className = "ImageListItemSelected";
  20:     $get("ImageListItemSelectedValue").value = Value;
  21: }

You may need to modify it slightly as I'm embedding a javascript file in there and trying to load it from Controls.ImageListBox.ImageListBox.js. As such, you may need to change where it's looking for that file. And if you have no idea how to do that, look here. Also, this version is really basic (can only select one item at a time, no animation, can only put one on a page due to naming of the hidden field, etc.) and was meant more as something to learn from as it does show some basics of creating a server control (like how to embed javascript, having child items for the control, etc.), so you may want to go in and modify it anyway. That being said, it renders everything such that you don't HAVE to modify anything. I've even added some CSS classes to the output so you can at least tell it how to render. For example, the above image used the following CSS:

   1: .ImageListBox
   2: {
   3:     width:100px;
   4:     height:100px;
   5: } 
   6:  
   7: .ImageListBoxTable
   8: {
   9:     width:100%;
  10: } 
  11:  
  12: .ImageListBoxTable tr td
  13: {
  14:     height:40px;
  15:     border-bottom:1px black solid;
  16: } 
  17:  
  18: .ImageListItemSelected
  19: {
  20:     background-color:Yellow;
  21: } 
  22:  
  23: .ImageListItem
  24: {
  25:     background-color:White;
  26: }

The ImageListBox is the div holding everything, ImageListTable is the table, ImageListItemSelected is the selected items, and ImageListItem is any item not selected. Other than that, you can simply drop it into your project using the following code:

   1: <cc1:ImageListBox ID="ImageListBox1" runat="server">
   2:     <Items>
   3:         <cc1:ImageListItem Selected="false" Text="..." Value="..." />
   4:         ...
   5:     </Items>
   6: </cc1:ImageListBox>

Pretty basic really. It just takes a list of ImageListItems in the Items field and you're set to go (each item has Selected, Text, and Value fields and are simply rendered as a bunch of TDs). Anyway, hopefully the code helps you out, so download it, give it a try, leave feedback, and happy coding.



Comments