Q&A
Display and Manage Images in ASP.NET
Develop image-management apps that exploit the .NET Framework and ASP.NET with recursion, the TreeView control, the System.Drawing.Images namespace, the System.IO namespace, and more.
Technology Toolbox: C#, ASP.NET
Q:
Manipulate Images in a Web App
I'm designing a Web application to display and manipulate images. The application should iterate through a directory structure that stores images and let users navigate the folders using a treeview-like interface. Images could be any size, so users might need to reduce them for speed purposesmaybe to thumbnail size. I used to be able to do all this with the GDI API; can I use ASP.NET instead?
A:
ASP.NET streamlines your image-processing chores. Use ASP.NET in conjunction with the .NET Framework, which exposes image-processing tools that leave the GDI API in the dust. The System.Drawing and System.Drawing.Images namespaces let you manipulate images, perform image streaming, and resize/reformat images.
Image-viewer applications should let users navigate through the images. A TreeView control works well; it lets you display a complete directory structure, retrieve a series of images, shrink them down, and load them into an array. I'll show you how to build a treeview in a Web Form that enumerates through a multilevel folder structure. You'll use recursion and other techniques to let users easily find and pick images to view.
You'll team up the TreeView and DataList controls to manage data, and you'll use them to load directory structures (with the System.IO namespace) and thumbnail images. You'll stream their thumbnails to a Web page using the data-bound ASP.NET DataList control. And you must remember to balance image file size against network bandwidth constraints. Fortunately, I know some tricks and workarounds I'll tell you about along the way.
Start by downloading the TreeView control (see Figure 1). It's part of the Microsoft Internet Explorer WebControls (IEWC) download, which also includes Toolbar, TabControl, and MultiPage UI controls. You don't get these controls in Visual Studio .NET.
The download's readme file guides you through installation. You copy the webctrl_client folder and its contents to the root of your ASP.NET app. These files contain the controls' behaviors and images. Then you copy the IEWC's .NET assembly to your app's bin folder. The assembly file, Microsoft.Web.UI.WebControls.dll, should appear in the project's References list (see Figure 2). Be sure to reference the IEWC as well. Then build the solution and try out the samples included with the IEWC.
Any ASP.NET project referencing the IEWC can use TreeView on a Web Form. I've included a sample app that reads a directory structure, using the System.IO namespace to locate its folders and JPG files (download all the sample apps in a single Web project).
Build Your Own App
Start building this sample app by creating an ASP.NET project in C# called PhotoWebApp. Add the IEWC controls and samples to the project, and reference the IEWC assembly. The code in the codebehind page iterates through the directory and builds the nodes for the TreeView server control. Put a node on the Web Form itself so you can use your TreeView control. Now tell the Web Form how to deal with the TreeView, starting with importing the TreeView namespace and assembly. You should also associate a tag prefix with the WebControls as a kind of shortcut to creating the tag controls. For example, create a tagprefix of "ie" so you can write the TreeView tag as <ie:TreeView>:
<%@ Register TagPrefix="ie"
Namespace="Microsoft.Web.UI.WebControls"
Assembly="Microsoft.Web.UI.WebControls"%>
<%@ Import Namespace="Microsoft.Web.UI.WebControls"
%>
<%@ Page language="c#" Codebehind="Treeview.aspx.cs"
AutoEventWireup="false"
Inherits="PhotoWebApp.Treeview"%>
Next, you define the basic treeview. Set the minimum attributes to identify the control's ID as tvTree, and tell your app this is a server control with the runat=server attribute and value. The SystemImagesPath attribute tells the TreeView control where it can find the images the control uses, such as the minus and plus images that expand and collapse treeview nodes:
<ie:treeview id="tvTree" runat="server"
SystemImagesPath="webctrl_client/1_0/treeimages/"
></ie:treeview>
Now sprinkle some code to set up and load the treeview in the codebehind. Add an inline frame element (iframe) as the target of the JPG files. When users click on a JPG file in the treeview, the image is sent to the iframe and rendered in it:
<iframe id="ifImages" name="ifImages"
frameBorder="yes"
width="100%" scrolling="auto" height="680">
</iframe>
The codebehind page for TreeView.aspx handles iterating through the image folder structure and loading the treeview with the appropriate files and folders. The project references the IEWC assembly, enabling the codebehind to import the Microsoft.Web.UI.WebControls' IEWC namespace, simplifying coding for the TreeView object. The page calls the LoadTreeView method when it loads. The method creates two instances of the TreeNodeType object: one to represent the folders, and one to represent the files.
Create the TreeNodeType object (oNodeTypeFolder) for the folder, then set its type to be a folder and set its image URLs to the corresponding images representing expanded and collapsed folders. Then add the TreeNodeType for the folder containing the treeview's TreeNodeTypes collection:
// Create the Tree Node Type for folders
string sIconImages =
"webctrl_client/1_0/Images/";
TreeNodeType oNodeTypeFolder = new
TreeNodeType();
oNodeTypeFolder.Type = "folder";
oNodeTypeFolder.ImageUrl = sIconImages +
"folder.gif";
oNodeTypeFolder.ExpandedImageUrl = sIconImages +
"folderopen.gif";
tvTree.TreeNodeTypes.Add(oNodeTypeFolder);
Create another TreeNodeType to represent the file nodes. The code is mostly the same, except that the file is a child node, obviating the need for two images (because you don't collapse and expand JPG files).
Use Recursive Looping
The LoadTreeView method moves on to build the treeview by looping recursively through the folder structure and loading the folders and files one at a time. The BuildTree method handles most of this functionality, accepting an argument telling the treeview where to start looking for the JPGs, and a second argument referencing an instance of the treeview's Nodes collection. Create a configuration setting in the Web.config file manually to store the starting point for where the images are stored. Use the Web.config file so you can change the location from one Web app to another without having to rebuild the project. Grab the starting point value by importing the System.Configuration namespace into the codebehind, then retrieve the setting:
string sGlobalImageFolder =
ConfigurationSettings.AppSettings.Get
("ImageFolder");
The BuildTree method iterates through the current folder (passed in as the sPath argument), searching for all folders and any JPGs. Use the System.IO namespace to access the .NET Framework's Directory class, which handles the functionality of retrieving and looping through the folder structure. Pass a path to GetDirectories (a Directory class method), and it returns an array of the folders and files contained within the given path. BuildTree grabs the folders into an array, then loops through each subfolder in the array. Invoke the CreateFolderNode method for each subfolder. This creates the folder node and adds it to the treeview's Nodes collection (see Listing 1).
BuildTree goes after the files once the folders immediately within the main path have been added to the treeview's Nodes collection. The Directory class has a GetFiles method, which accepts two arguments: one for the path to search, and the other for the filename pattern to match. GetFiles returns an array of the JPGs in the current path's folder. BuildTree traverses this array of files one by one, adding the file node to the treeview's Nodes collection.
So far, BuildTree has retrieved only one level at a time, but it can grab folders and files several levels deep. Now you can use it to iterate through the treeview's nodes, looking for the subfolders within the current folder. It invokes the BuildTree method recursively when it finds one, passing the method to the folder one level deeper. This process continues for all the folder levels, building a recursive call stack. It unwinds itself eventually when there are no more folders in the branch. Recursion is a great technique, but make sure you include an out clause when implementing recursive algorithms. The out clause in this algorithm calls itself only for as many levels of subfolders as there are.
Create Your File Nodes
The CreateFileNode and CreateFolderNode methods show how easily you can create a folder or a file node (see Listing 2). Start by creating a TreeNode object. Set its type to be a file (one of the types you established in the LoadTreeView method), then set the text for the node and the NavigateUrl property. NavigateUrl tells TreeView what URL to navigate to when this node is selected. In this case, set the NavigateUrl to link to the JPG the node represents. You use the iframe created in the ASPX page to control where the image appears. The file node's Target property is set to the iframe control, telling the JPG image to render in the iframe to the right of the treeview.
This sample application performs well in small-scale situations, but you should consider more scalable solutions if you need to load hundreds of folders and files in the image path. For example, you could load the treeview with folders and its subfolders, but not with the image files. The treeview then would create only nodes for the foldersusually much more manageable in number.
However, this technique would mandate using another approach to display your JPGs. You couldn't link the file nodes to render JPGs in an iframe, because you wouldn't have file nodes in this solution. Instead, you'd set the folder nodes to load all the images within that folder in the iframe. This in turn would require you to tackle the issue of large image file sizes. Loading several of them in an iframe would likely overrun your network's bandwidth as well as screen real-estate limits. You can solve both problems by creating image thumbnails and displaying them using an ASP.NET DataList (see Figure 3). I've included the information you need to build this solution in the sidebar, "Conserve Bandwidth With Thumbnails." It lets users see a large number of image thumbnails at once and load the ones they choose quickly.
Some server controls have a large footprint and can degrade performance, but if you use them judiciously, you can combine feature-rich server controlssuch as TreeView and DataListto yield robust applications.
About the Author
John Papa is a Microsoft Regional Director and former Microsoft technical evangelist. Author of 100-plus articles and 10 books, he specializes in professional application development with Windows, HTML5, JavaScript, CSS, Silverlight, Windows Presentation Foundation, C#, .NET and SQL Server. Check out his online training with Pluralsight; find him at johnpapa.net and on Twitter at twitter.com/john_papa.