Blogs

Paul Kimmel's Blog

Using the ASPxUploadControl and Displaying Image Thumbnails

     

I am surprised at the myriad ways in which companies make money. I wrote some software a few years ago for a company that sold millions of dollars worth of labels. Labels for medicine, warnings, and all kinds of other things. The application was pretty simple. The application permitted the user to add, browse, and edit the text content of the images. The fun part was that I integrated Adobe Illustrator into the application and used Illustrator CS3 to read the text layer from the image, spell check it, and search the image database by things like the text on the image. The application wasn’t very complicated but it was fun writing code that used Illustrator as an application servlet and playing with images, thumbnail versions of those images, and creating a couple of custom components.

This blog entry shows you how to use DevExpress’ ASPxUploadControl to upload images to a server, create a thumbnail version of those images, and display the thumbnail versions of the uploaded image. The example includes a couple of examples of our client-side event functionality, some JavaScript, and a little GDI+ mojo to round out the blog. I hope you have fun tinkering with the code.

Constructing the ASPX Bits

The Web page was constructed with an HTML table for layout, an <img> element for the uploaded graphic and version 9.2 of our ASPxUploadControl and ASPxButton. The HTML table uses the table cell <td> rowspan to allow room for the image and colspan to center the upload button. The ASPX is in Listing 1, and the design time view is shown Figure 1.

Listing 1: The ASPX describing the simple layout using an HTML table and the Web controls.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="DevExpress.Web.v9.2, Version=9.2.4.0, Culture=neutral,
PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.Web.ASPxCallbackPanel" TagPrefix="dxcp" %>
<%@ Register Assembly="DevExpress.Web.v9.2, Version=9.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"    Namespace="DevExpress.Web.ASPxCallback" TagPrefix="dxcb" %>
<%@ Register assembly="DevExpress.Web.ASPxEditors.v9.2, Version=9.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxEditors" tagprefix="dxe" %>
<%@ Register assembly="DevExpress.Web.v9.2, Version=9.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxUploadControl" tagprefix="dxuc" %>
<%@ Register assembly="DevExpress.Web.v9.2, Version=9.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxPanel" tagprefix="dxp" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
 
    <div>
      <table width="50%">
        <tr>
            <td>
                <dxuc:ASPxUploadControl ID="ASPxUploadControl1" runat="server" 
                    ClientInstanceName="uploader" 
                    onfileuploadcomplete="ASPxUploadControl1_FileUploadComplete">
                    <ClientSideEvents FileUploadComplete="function(s, e) {
                
     debugger;
                 
    _aspxGetElementById('PreviewImage').src = e.callbackData;
                   
}" />
                </dxuc:ASPxUploadControl>
            </td>
            <td rowspan="2">
                <img alt="" src="" id="PreviewImage"/></td>
        </tr>
       
<tr>
     
    <td>
 
          <dxe:ASPxButton ID="ASPxButton1" runat="server" Text="Upload"
                  AutoPostBack="False">
 
             <ClientSideEvents Click="function(s, e) {
 
              uploader.UploadFile();
             
}" />
           
</dxe:ASPxButton>
         
</td>
       
</tr>
     
</table>
     
</div>
   
</form>
</body>
</html>

image
Figure 1: The HTML table layout, <img>, and the ASPxButton and ASPxUploadControl as laid out at design time.

You don’t necessarily have to use an HTML table for layout. You could use a style sheet and div tags for example. Also, the design time layout can be pretty basic if you use a style sheet and skins again to manage the appearance. (A good place to learn how to learn more about style sheets is chapter 10 of the Professional DevExpress ASP.NET Controls and some of the other great blogs and articles on the devexpress web site. Mehul Harry and Jeff in production do a great job with some of the video demos.)

Implementing the Client-Side Events

A great feature of DevExpress ASP.NET controls is the client-side script functionality. With DevExpress controls there is an analogous client-side control for each server-side control. The convention is ASPxClientControlName. For example, the ASPxButtonControl has its paired ASPxClientButtonControl. So, if you set the ClientInstance name for the ASPxButtonControl to uploader then when you are interacting with the control in client script you are invoking behaviors and managing properties via an instance of the ASPxClientButtonControl.

To implement client-side functions for DevExpress controls click the ClientSideEvents property of a particular control and use the Client-Side Events Editor to implement a specific event (see Figure 2). To initiate the file upload a Click function was associated with the ASPxButton, and a FileUploadComplete was implemented—see Listing 1 for both functions—to respond when the upload has completed.

image
Figure 2: The Client-Side Events Editor makes it easy to associate client-side event functions with a particular ASPx control.

Client-side events are defined as strings and attached on the client through an AddHandler call. You don’t have to manage this plumbing; its all managed for you. However, if you want to know how client-side events are wired up at runtime then here you go. The controls emit some script that obtains a reference to the relevant control. For instance when an event needs to be wired for the client-side version of an ASPxUploadControl a reference is obtained to its paired ASPxClientUploadControl. The reference is used to refer to the particular client event property and AddHandler is called. AddHandler gets the inline code that you associated with event as text using the Client-Side Event Editor. Here is a chunk of the generated script showing you how the event handler for the ASPxUploadControl’s Click event is wired.

var dxo = new ASPxClientUploadControl('ASPxUploadControl1');
window['uploader'] = dxo;
aspxAddDisabledItems('ASPxUploadControl1',[[['dxucDisabled dxucDisabledButton'],[''],['Add','Upload','Cancel','Remove_0']]]);
dxo.FileUploadComplete.AddHandler(function(s, e) {
 
debugger;
 
_aspxGetElementById('PreviewImage').src = e.callbackData;
});

Keep in mind that there is all kinds of background processing going on when a ASP.NET Web page is actually pushed out to the client, so this approach is not unique. And, of course, all of this plumbing is automatic. You don’t have to worry about the how; just implement client-side code using the Client-Side Event Editor and the rest is managed for you.

The ASPxButtonClick on the client-side calls UploadFile which will result in the ASPxUploadControl.FileUploadComplete being fired on the server and then the client-side FileUploadComplete being called. The client-side FileUploadComplete handler obtains a reference to the <img> element and sets it’s src property to the data return from the server-side FileUploadComplete. The src is an URL to the image to display. What has to happen in between is that the uploaded file needs to be saved, and for our example a thumbnail image of the control needs to be created.

Implementing the Code-Behind

As implemented in Listing 2 the code-behind saves the uploaded image and creates a thumbnail version of the image to display. On the server FileUploadComplete is called first—in this scenario. FileUploadComplete calls SavePostedFile and assigns a predetermined path and filename format to the FileUploadCompleteEventArgs.CallbackData property. The original filename with a “thumbnail_” prefix is designated as the return value—the thumbnail to display.

SavePostedFile checks to make sure that the uploadedFile is valid. If UploadedFile.IsValid then UploadedFile.SaveAs is called saving a copy of the uploaded file to a folder on the server; in this instance the virtual path ~/Images/. MapPath is called to map the virtual folder to a physical folder for the SaveAs method. CreateThumbnail is called to create a thumb-image of the uploaded file.

CreateThumbnail creates an in-memory instance of the image and assigns it to a GDI+ image. (You can pass the bytes as I did and use FromStream and create a MemoryStream or use the UploadedFile.FileContent property, which is a stream.) The GDI+ Image calls GetThumbnailImage to create a thumbnail image version of the uploaded image. GetThumbnailImage makes a call into unmanaged code, which is why the delegate for errors and the IntPtr.Zero arguments are used. The delegate and IntPtr arguments are unused but have to be provided. (You can use Reflector to see the call into unmanaged code under the hood.) Finally, the thumbnail is saved and the client-side FileUploadComplete event takes over displaying the thumbnail image (see Figure 3).

Listing 2: The code-behind that

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using GDI=System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Drawing.Imaging;
using DevExpress.Web.ASPxUploadControl;
using System.Drawing;

public partial class _Default : System.Web.UI.Page
{
    private string path = "~/Images/";
    private string current = "thumbnail_{0}";
    private void CreateThumbnail(byte[] data, string target)
    {
      GDI.Image image = GDI.Image.FromStream(new MemoryStream(data));
      GDI.Image thumbnail =
        image.GetThumbnailImage(100, 100,
        delegate { return false; },IntPtr.Zero);
      thumbnail.Save(GetUploadedTargetName(target));
    }

    private string GetUploadedTargetName(string filename)
    {
      return MapPath(path) + string.Format(current, filename);
    }

     protected void SavePostedFile(UploadedFile uploadedFile)
     {
       if (!uploadedFile.IsValid) return;
       uploadedFile.SaveAs(MapPath(path) + uploadedFile.FileName);
       CreateThumbnail(uploadedFile.FileBytes, uploadedFile.FileName);
     }

    protected void  ASPxUploadControl1_FileUploadComplete(object sender,
      DevExpress.Web.ASPxUploadControl.FileUploadCompleteEventArgs e)
    {
      SavePostedFile(e.UploadedFile);
      e.CallbackData = "Images/" + string.Format(current, e.UploadedFile.FileName);
    }
}

image

Figure 3: The uploaded image as a thumbnail  to the right of the ASPxUploadControl.

The basic orchestration of the upload, save image, create thumbnail, and display thumbnail is:

  • call ASPxUploadControl.UploadFile from the client. You can use your own button or set ASPxUploadControl.ShowUploadButton to true
  • implement a server-side ASPxUploadControl.FileUploadComplete method to save the uploaded file and create the thumbnail
  • implement a client-side ASPxUploadControl.FileUploadComplete to update the image element’s src property. Alternatively, you could set the runat=”server” attribute of the <img> element and set the src property on the server side, which eliminates the need for the client-side FileUploadComplete method

You can play around with variations of my implementation. For example, set the <img> element’s runat=”server” attribute and try setting the image’s src property on the server. Some things to try, include using the same filename for the thumbnail each time. Does it effect whether or not the image is updated on the client? What if you use the ASPxBinaryImage or ASPxImage: do you need to change any of the client or server code?

I like to play with implementation variations. The main objective is to fine tune the results and ultimately get a solution as simple and short as possible.

Enjoy.

Published Sep 29 2009, 09:36 PM by Paul Kimmel (DevExpress)
Bookmark and Share

Comments

 

Alvaro Torres said:

Great job !!!

September 29, 2009 7:29 PM
 

Ralph Jansen said:

I don't really understand what you mean by the GDI+ stuff. We had a problem with getting images from a database. We had a list and in that list we had an imagecontrol. That imageurl refered to another page. That page was quering to the image, transformed it and returned it back.

Sometimes we get some GDI+ errors that we don't understand

September 30, 2009 9:44 AM
 

Paul Kimmel (DevExpress) said:

Ralph Jansen:

The upcoming book has some goodies about images from a database. If you can be more specific maybe I can help. paulk@devexpress.com

September 30, 2009 1:23 PM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.