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:
<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>
<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>
</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>
<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>
</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:
<%@ 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 DIV
s, 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:
<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:
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:
protected void Page_Load(object sender, EventArgs e)
{
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:
protected void btnSubmit_Click(object sender, EventArgs e)
{
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>");
}
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:
<script type="text/javascript">
<!--
var g_ImageList1;
var g_ImageList2;
var g_txtItem;
var g_btnNew;
var g_btnCopy;
function pageLoad()
{
g_ImageList1 = $find("<%= ImageList1.ClientID %>");
g_ImageList2 = $find("<%= ImageList2.ClientID %>");
if(g_ImageList1)
g_ImageList1.add_remove(onRemove);
g_txtItem = $get("txtItem");
g_btnNew = $get("btnNew");
g_btnCopy = $get("btnCopy");
if(g_btnNew)
$addHandler(g_btnNew, "click", onBtnNew);
if(g_btnCopy)
$addHandler(g_btnCopy, "click", onBtnCopy);
}
function pageUnload()
{
if(g_ImageList1)
g_ImageList1.remove_remove(onRemove);
if(g_btnNew)
$clearHandlers(g_btnNew);
if(g_btnCopy)
$clearHandlers(g_btnCopy);
}
function onRemove(sender, args)
{
if((g_ImageList2) && (g_ImageList2.get_item(args.get_key())))
g_ImageList2.remove_item(args.get_key());
}
function onBtnNew()
{
if(g_ImageList1 && g_txtItem)
{
var item = new Memba.WebControls.ImageListItem(
Memba.Utils.newGuid(),
'<%= this.ResolveClientUrl("~/images/angel.gif") %>',
g_txtItem.value
);
g_ImageList1.add_item(item);
}
}
function onBtnCopy()
{
if(g_ImageList1 && g_ImageList2)
{
var _arr = Array.clone(g_ImageList1.get_items());
for(var i = 0; i < _arr.length; i++)
{
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.