Introduction
ImageList and MultiUpload have recently been described in two articles:
Our objective is to realize a professional GUI for uploading files, and this is achieved by combining ImageList
and MultiUpload
. Our environment is ASP.NET 2.0 on Windows and IIS, and both our ImageList
and MultiUpload
controls use ASP.NET AJAX Extensions 1.0, which you can download and install 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 ImageList
and a MultiUpload
controls 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
and MultiUpload2
server controls. 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 both the ImageList
and MultiUpload2
server controls, either by dragging and dropping the controls after adding them to the toolbox, or simply by adding the following code between the existing <form>
tags:
<form id="form1" runat="server" enctype="multipart/form-data">
<asp:ScriptManager ID="ScriptManager" runat="server">
<Scripts>
<asp:ScriptReference Path="~/scripts/Memba.Utils.js" />
</Scripts>
</asp:ScriptManager>
<mbui:MultiUpload2 ID="MultiUpload" runat="server"
Text="Choose file..."
Width="100px"
CssClass="cssMultiUpload"
HoverCssClass="cssMultiUploadHover">
</mbui:MultiUpload2>
<mbui:ImageList ID="ImageList" runat="server"
CssClass="cssList"
ItemCssClass="cssItem"
ItemHoverCssClass="cssItemHover"
ImageCssClass="cssImage"
TextCssClass="cssText"
RemoveCssClass="cssRemove"
RemoveTooltip="Remove from selection"
LinesOfText="2"
Height="92px"
Width="420px">
</mbui:ImageList>
<input type="button" id="ClearButton" value="Clear" onclick="onClear();" />
<asp:Button ID="SubmitButton" runat="server" Text="Submit" OnClick="SubmitButton_Click" />
</form>
This form also includes an HTML button (ClearButton
) and an ASP.NET Submit button (SubmitButton
).
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.
For file uploads to work, you need to make two important settings:
- Define
enctype="multipart/form-data"
on the form
tag; - Add an
httpRuntime
section to the <system.web>
section of your web.config. This is extensively described in the articles referenced in the introduction above.
The inline CSS styles used for the presentation of ImageList
and MultiUpload
are also described in the corresponding articles. After adding the styles, your page in Design mode should look like:
Now that our page is designed, we need the code to save the posted files. Double click the Submit button (btnSubmit
) to implement the server-side Click
event handler in Default.aspx.cs:
private const string TARGET_DIR = "~/";
protected void SubmitButton_Click(object sender, EventArgs e)
{
string sTargetDir = Request.MapPath(TARGET_DIR);
System.Diagnostics.Debug.Assert(Directory.Exists(sTargetDir));
for (int i = 0; i < Request.Files.Count; i++)
{
HttpPostedFile objFile = Request.Files[i];
if ((objFile.ContentLength > 0) && (objFile.FileName.Length > 0))
{
string sFileName = Path.GetFileName(objFile.FileName);
string sFilePath = Path.Combine(sTargetDir, sFileName);
FileInfo objFileInfo = new FileInfo(sFilePath);
if (objFileInfo.Exists)
{
objFileInfo.Attributes &= ~FileAttributes.ReadOnly;
objFileInfo.Delete();
}
objFile.SaveAs(sFilePath);
}
}
ImageList.ImageListItemCollection.Clear();
}
Add the following statement at the top of the file:
using System.IO;
The important thing to note here is that the browser clears HTML file input controls each time it loads the page, especially after a postback. There is no way to set the value of a file input control, otherwise web servers could obtain user files without their consent. There is no workaround to this security restriction. Our ImageList
has been designed to maintain its state, so it needs to be cleared to be in sync with the MultiUpload
control.
We could run the page at this stage and upload files, but we need some JavaScript client code to display, in the ImageList
, the files which have been selected in the MultiUpload
control. Add the following script just before the </body>
closing tag of your page:
<script type="text/javascript">
<!--
var g_MultiUpload;
var g_ImageList;
function pageLoad()
{
g_MultiUpload = $find("<%= MultiUpload.ClientID %>");
if(g_MultiUpload)
g_MultiUpload.add_browse(onBrowse);
g_ImageList = $find("<%= ImageList.ClientID %>");
if(g_ImageList)
g_ImageList.add_remove(onRemove);
}
function pageUnload()
{
if(g_MultiUpload)
g_MultiUpload.remove_browse(onBrowse);
if(g_ImageList)
g_ImageList.remove_remove(onRemove);
}
function onBrowse(sender, args)
{
if((g_ImageList) && (g_MultiUpload))
{
if (g_ImageList.find_item(args.get_value()).length > 0)
{
alert("file already in list");
g_MultiUpload.removeInput(args.get_id());
}
else
{
var item = new Memba.WebControls.ImageListItem(
Memba.Utils.newGuid(),
'<%= this.ResolveClientUrl("~/images/upload.gif") %>',
args.get_value(),
args.get_value(),
args.get_id()
);
g_ImageList.add_item(item);
}
Sys.Debug.trace(g_ImageList.get_count()
+ " files in image list, and " + g_MultiUpload.get_count()
+ " files in MultiUpload control");
}
}
function onRemove(sender, args)
{
if((g_ImageList) && (g_MultiUpload))
{
g_MultiUpload.removeInput(args.get_tag());
Sys.Debug.trace(g_ImageList.get_count()
+ " files in image list, and " + g_MultiUpload.get_count()
+ " files in MultiUpload control");
}
}
function onClear()
{
if(g_MultiUpload)
g_MultiUpload.clear();
if(g_ImageList)
g_ImageList.clear();
}
</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 above implements event handlers for the Browse
event of the MultiUpload
control, for the Remove
event of the ImageList
control, and for the Click
event of the Clear button. The Browse
event handler adds the selected file to the ImageList
. ImageList
items have a property called tag
, which is set to the identifier of the file input in the MultiUpload
control. The Remove
event handler reads the tag
of the item removed from the ImageList
to remove the corresponding file input from the MultiUpload
control. The Clear
event handler simply clears both the ImageList
and the MultiUpload
controls.
Press F5 to run the project, and click the MultiUpload
control labeled “Choose file...” to add a file to the list. Move your mouse over the corresponding item in the ImageList
to display the remove icon which is a red cross. Click the remove icon to remove the corresponding file from the upload selection. Add more files. Test the Clear button. Add files, and submit to upload the files.
At this stage, finalizing a professional upload page would require a progress indicator. We recommend reading Using AJAX to display the progress of an ASP.NET server task. If you want to combine all these controls together, I recommend downloading the source code of the Memba Velodoc XP Edition and the developer tutorial included, which will help you get to the next stage.
Points of interest
This MultiUpload
control hides the complexity of dynamically creating file input HTML controls, and removes any constraint regarding the presentation of the files which have been selected for upload. The combination with the ImageList
control is a great presentation option described here, which is a huge improvement over old, ugly file input controls. For more advanced developers, the source code for the ImageList
and MultiUpload
controls is available at Codeplex, Google code, and Sourceforge.NET, and is commented in great details.
History
- Version 1.0 - Dated 20 Dec 2007.