Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / IIS

An AJAX image list control to manage lists of items displayed with icons

2.00/5 (2 votes)
18 Dec 2007GPL34 min read 1   359  
An ASP.NET server control inspired from WinForms’ ListView and implementing ASP.NET AJAX Extensions 1.0.

ImageList in run mode

Introduction

Our objective is to provide a means to manage small lists of items in the browser, including adding and removing items, where the final list can be posted to an ASP.NET server. This function is fulfilled by an ImageList control, which is inspired from the WinForms’ ListView control. Our environment is ASP.NET 2.0 on Windows and IIS, and our image list uses ASP.NET AJAX Extensions 1.0, which you can download and installed from here.

Background

This article refers to the open-source controls of the “Memba Velodoc XP Edition”, which you can download from here (this page provides links to Codeplex, Google code, and Sourceforge.NET) and which are distributed under the GPL license. These controls include an image list which we use in this article. You can experiment these controls live at this link.

Using the code

In Visual Studio 2005, create a new ASP.NET AJAX-Enabled Web Site, and add a reference to Memba.WebControls.XP.dll which contains the ImageList server control. Memba.WebControls.XP.dll is part of the Memba Velodoc XP Edition. The source code is available at the download location cited above.

Open the Default.aspx page and add the ImageList server control, either by dragging and dropping the control after adding it to the toolbox, or simply by adding the following code between the existing <form> tags:

ASP.NET
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/scripts/Memba.Utils.js" />
    </Scripts>
</asp:ScriptManager>
<table border="0" cellpadding="0" cellspacing="5">
    <tr>
        <td>
            <!-- ImageList -->
            <mbui:ImageList ID="ImageList1" runat="server"
                CssClass="cssList"
                ItemCssClass="cssItem"
                ItemHoverCssClass="cssItemHover"
                ImageCssClass="cssImage"
                TextCssClass="cssText"
                RemoveCssClass="cssRemove"
                RemoveTooltip="Remove from selection"
                LinesOfText="2"
                Height="92px"
                Width="300px">
            </mbui:ImageList>
            <!-- ImageList -->
        </td>
        <td align="center">
            <input id="txtItem" type="text" value="new item"
                style="width:100px;" />
            <input id="btnNew" type="button" value="New"
                style="width:100px;" /><br /> 
            <input id="btnCopy" type="button" value="Copy >>"
                style="width:100px;" /><br />
            <asp:Button ID="btnSubmit" runat="server" Text="Submit"
                style="width:100px;" OnClick="btnSubmit_Click" />
        </td>
        <td>
            <!-- ImageList -->
            <mbui:ImageList ID="ImageList2" runat="server"
                CssClass="cssList"
                ItemCssClass="cssItem"
                ItemHoverCssClass="cssItemHover"
                ImageCssClass="cssImage"
                TextCssClass="cssText"
                RemoveCssClass="cssRemove"
                RemoveTooltip="Remove from selection"
                LinesOfText="2"
                Height="92px"
                Width="300px">
            </mbui:ImageList>
            <!-- ImageList -->
        </td>
    </tr>
</table>
<textarea id="TraceConsole" cols="100" rows="20"></textarea>
</form>

The form also includes an HTML text box (txtItem), two HTML buttons (btnNew and btnCopy), and an ASP.NET submit button (btnSubmit).

You may have to register the tag prefix at the top of the page, using the following statement:

ASP.NET
<%@ Register Assembly="Memba.WebControls.XP" 
    Namespace="Memba.WebControls" TagPrefix="mbui" %>

Make sure you add a script reference to Memba.Utils.js, which contains a necessary utility function to create GUIDs in JavaScript.

The ImageList control is a DIV which embeds a series of floating DIVs, each representing an item. Each item DIV contains an image, a label, and a remove icon. Add the following styles just above the </head> closing tag of your page:

HTML
<style type="text/css">
<!--
div.cssList
{
    position:fixed;
    overflow:auto;
    height:92px;
    border:1px solid #0F0000;
    background-color:#FFFFFF;
    clear:both;
}
div.cssItem
{
    position:relative;
    width:70px;
    height:70px;
    padding:5px;
    margin:5px;
    text-align:center;
    border:1px solid #FFFFFF;
    background-color:#FFFFFF;
    float:left;
}
div.cssItemHover
{
    position:relative;
    width:70px;
    height:70px;
    padding:5px;
    margin:5px;
    text-align:center;
    border:solid 1px #DCDCDC;
    background-color:#FAFAFA;
    float:left;
}
img.cssImage
{
    height:32px;
    width:32px;
    border:0px;
}
div.cssText
{
    font-family:Arial, Helvetica, sans-serif;
    font-size:9px;
    white-space:normal;
}
img.cssRemove
{
    position:absolute;
    top:64px;
    left:64px;
    height:16px;
    width:16px;
    border:0px;
    z-index:10;
    cursor:pointer;
}
//-->
</style>

Your ImageList control should now look like:

ImageList in design mode

Obviously, you can set the list content using server-side code. Open the code-behind file named Default.aspx.cs, and implement the Page_Load event handler as follows:

C#
protected void Page_Load(object sender, EventArgs e)
{
    //Initialize ImageList1 with a new item
    ImageList1.ImageListItemCollection.Add(
        new Memba.WebControls.ImageListItem(
            Guid.NewGuid().ToString(),
            this.ResolveClientUrl("~/images/angel.gif"),
            "item #1"
        )
    );
}

Press F5 to run the page, and check that you have an item in the list. You will need an image file in the location designed in the piece of code above.

Double click the ASP.NET submit button, and implement the Click event handler as follows:

C#
protected void btnSubmit_Click(object sender, EventArgs e)
{
    //List the content of ImageList1
    Response.Write("<strong>ImageList1</strong>");
    if (ImageList1.ImageListItemCollection.Count > 0)
    {
        Response.Write("<ul>");
        foreach (Memba.WebControls.ImageListItem objImageListItem
            in ImageList1.ImageListItemCollection)
        {
            Response.Write("<li>" + 
              objImageListItem.Text + "</li>");
        }
        Response.Write("</ul>");
    }
    //List the content of ImageList2
    Response.Write("<strong>ImageList2</strong>");
    if (ImageList2.ImageListItemCollection.Count > 0)
    {
        Response.Write("<ul>");
        foreach (Memba.WebControls.ImageListItem objImageListItem
            in ImageList2.ImageListItemCollection)
        {
            Response.Write("<li>" + 
              objImageListItem.Text + "</li>");
        }
        Response.Write("</ul>");
    }
}

Click F5 to run the page, and click the Submit button. The response should display the content of both image lists in bulleted lists. Observe that on post back, the image lists keep their state.

The ImageList control stores an array of items, serialized with JSON, in an HTML hidden field:

  • When you add items server-side on page load, these items are simply added to the hidden field;
  • When the page renders in the browser, the JavaScript code built into the control reads the serialized array in the hidden field to render the images;
  • When using the client-side API of the control, you simply add or remove items to the array serialized into the hidden field;
  • When you post back the page, the hidden field is posted back, and the control’s server-side code gives you access to the collection of items, which it reads from the hidden field.

The contents of the image lists can also be changed client-side using JavaScript code. Add the following script just before the </body> closing tag of your page:

JavaScript
<script type="text/javascript">
<!--
// Declare global variables for the various controls
var g_ImageList1;
var g_ImageList2;
var g_txtItem;
var g_btnNew;
var g_btnCopy;
//pageLoad function of ASP.NET Ajax Extensions framework
function pageLoad()
{
    //get a reference to the ImageList controls
    g_ImageList1 = $find("<%= ImageList1.ClientID %>");
    g_ImageList2 = $find("<%= ImageList2.ClientID %>");
    //Add event handlers for the ImageList controls
    if(g_ImageList1)
        g_ImageList1.add_remove(onRemove);
    //Get references to html controls
    g_txtItem = $get("txtItem");
    g_btnNew = $get("btnNew");
    g_btnCopy = $get("btnCopy");
    //Add event handlers for the click event of both html buttons
    if(g_btnNew)
        $addHandler(g_btnNew, "click", onBtnNew);
    if(g_btnCopy)
        $addHandler(g_btnCopy, "click", onBtnCopy); 
}
//pageUnload function of ASP.NET Ajax Extensions framework
function pageUnload()
{
    //Clear event handlers
    if(g_ImageList1)
        g_ImageList1.remove_remove(onRemove);
    if(g_btnNew)
        $clearHandlers(g_btnNew);
    if(g_btnCopy)
        $clearHandlers(g_btnCopy); 
}
//Remove event handler for the ImageList controls
function onRemove(sender, args)
{
    if((g_ImageList2) && (g_ImageList2.get_item(args.get_key())))
        g_ImageList2.remove_item(args.get_key());
}
//click event handler for the btnNew button
function onBtnNew()
{
    if(g_ImageList1 && g_txtItem)
    {
        //Instantiate a new item
        var item = new Memba.WebControls.ImageListItem(
            Memba.Utils.newGuid(),
            '<%= this.ResolveClientUrl("~/images/angel.gif") %>',
            g_txtItem.value
            );
        //Add the item to the list
        g_ImageList1.add_item(item);
    }
}
//click event handler for the btnCopy button
function onBtnCopy()
{
    if(g_ImageList1 && g_ImageList2)
    {
        //Clone the array in ImageList1
        var _arr = Array.clone(g_ImageList1.get_items());
        for(var i = 0; i < _arr.length; i++)
        {
            //Copy any non-existing item to ImageList2
           if(g_ImageList2.get_item(_arr[i].get_key()) == null)
               g_ImageList2.add_item(_arr[i]);
        }
    }
}
//-->
</script>

ASP.NET AJAX extensions provide two important JavaScript event handlers:

  • pageLoad is called by the framework when the page DOM and scripts are loaded and initialized. This is a good place to get references to controls and add event handlers.
  • pageUnload is called by the framework when the page unloads. It is recommended to clear handlers at this stage.

The script implements event handlers for the Click event of the two HTML buttons (btnAdd and btnCopy). The Click event handler for btnAdd adds a new item to the first image list. The Click event handler for btnCopy copies the contents of ImageList1 to ImageList2.

The script also implements an event handler for the remove event of the first image list. Each time an item is removed from the first image list, it is also removed from the second image list, provided it exists.

Press F5 to run the project, and click the various buttons to experiment with the ImageList.

Points of interest

This image list is a flexible and user-friendly alternative to list boxes for managing small lists of items. For more advanced developers, the source code of the image list is available at Codeplex, Google code, and Sourceforge.NET, and is commented in great details.

History

  • Version 1.0 - dated 18 Dec 2007.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)