Thursday, October 2, 2008

Image browser with ASP.NET Ajax

Introduction

I have written an image browser page with ASP.NET, using ASP.NET Ajax to retrieve information about images on the server. Images are displayed using a single aspx page and a folder with jpg files. The code-behind is written in C#. No database is involved.

Background


My goal was to show my family some photos on a web page and hopefully spending less than a few hours accomplishing that. So I wanted to do so without using a database, without titles, without descriptions, without tags, etc. However, I included the shooting date of the photo as part of the file name, since it provided a way to retain som information without using a database.

You see the resulting web page on the right.

The code

Code-behind (C#)

I first created a aspx web form, and in the code-behind wrote a static web method to return information about the images on the server:
[WebMethod]
public static Image[] GetImages()
{
// The virtual path to the image folder:
// (in this case a folder that contains some photos)
string imageFolderVirtualPath = "~/Photos/";
string imageFolderPath = HttpContext.Current.Server.MapPath(imageFolderVirtualPath);
List<image> images = new List<image>();
DirectoryInfo diImages = new DirectoryInfo(imageFolderPath);
// Only *.jpg files are included in this case:
foreach (FileInfo fiImage in diImages.GetFiles("*.jpg"))
{
string fileName = fiImage.Name;
// If the file name starts with the DateTime pattern yyyy-MM-dd,
// the date is parsed from that:
string date = string.Empty;
int year = 0;
int month = 0;
int day = 0;
if (fileName.Length > 9 && Int32.TryParse(fileName.Substring(0, 4), out year)
&& Int32.TryParse(fileName.Substring(5, 2), out month)
&& Int32.TryParse(fileName.Substring(8, 2), out day))
{
date = new DateTime(year, month, day).ToLongDateString();
}
images.Add(new Image
{
Date = date,
VirtualPath = CombineVirtualPaths(imageFolderVirtualPath, fileName)
});
}
return images.ToArray();
}

A helper method combines the virtual paths:
private static string CombineVirtualPaths(string virtualPath1, string virtualPath2)
{
return string.Format("{0}/{1}", virtualPath1.Trim('~', '/'), virtualPath2.Trim('/'));
}

A helper class contains information about an image:
public class Image
{
/// <summary>
/// The virtual path to the image file
/// </summary>
public string VirtualPath { get; set; }

/// <summary>
/// The date as a string
/// </summary>
public string Date { get; set; }
}


html/aspx markup

In the *.aspx file I have the following markup:
<body onload="GetImages();">
<form id="form1" runat="server">
<asp:ScriptManager id="sm1" runat="server" EnablePageMethods="true" EnableScriptGlobalization="true">
</asp:ScriptManager>
<div>
<div id="divButtons" style="float: left;">
</div>
<div id="divImage" style="float: left;">
</div>
</div>
</form>
</body>


Javascript

On load fires a javascript method that retrieves info from the page method:
// Information about the Images:
var images;

// The index of the currently shown Image:
var currentImageIndex = 0;

// Calls the page method to obtain information about the Images:
function GetImages() {
PageMethods.GetImages(GetImagesCompleted);
}

// The call-back where information about the images is returned:
function GetImagesCompleted(result) {
images = result;
ShowImage();
}

// Shows the Image:
function ShowImage() {
var currentImage = images[currentImageIndex];
var date = currentImage.Date;
var imgImage = "<img id='imgImage' alt='" + date +
"' title='" + date + "' src='" + currentImage.VirtualPath "' />";
var dp = document.getElementById("divImage");
dp.innerHTML = imgImage;
document.title = date;
ShowButtons();
}

A separate javascript method displays navigation buttons as appropriate:
// Shows the buttons:
function ShowButtons() {
// Gets localized texts (English or Danish) for the buttons.
// This only works with the following two settings:
// - The ScriptManager on this page must have: EnableScriptGlobalization="true"
// - web.config must have: <globalization culture="auto"> (in <system.web> section)
// The default English texts:
var first = "First";
var previous = "Previous";
var next = "Next";
var last = "Last";
if (Sys.CultureInfo.CurrentCulture.name.toLowerCase().startsWith("da")) {
// The Danish texts:
first = "Første";
previous = "Forrige";
next = "Næste";
last = "Sidste";
}
var button1 = "<div><input type='button' style='width: 75px;'";
var btnFirst = button1 + " value='" + first + "' onclick='ShowFirstImage();'";
var btnPrevious = button1 + " value='" + previous + "' onclick='ShowPreviousImage();'";
var btnNext = button1 + " value='" + next + "' onclick='ShowNextImage();'";
var btnLast = button1 + " value='" + last + "' onclick='ShowLastImage();'";
if (currentImageIndex == 0) {
btnFirst += " disabled='disabled'";
btnPrevious += " disabled='disabled'";
}
if (currentImageIndex == images.length - 1) {
btnNext += " disabled='disabled'";
btnLast += " disabled='disabled'";
}
var button2 = " /></div>";
btnFirst += button2;
btnPrevious += button2;
btnNext += button2;
btnLast += button2;
var db1 = document.getElementById("divButtons");
db1.innerHTML = btnFirst + btnPrevious + btnNext + btnLast;
}

// Shows the first Image:
function ShowFirstImage() {
currentImageIndex = 0;
ShowImage();
}

// Shows the previous Image:
function ShowPreviousImage() {
if (currentImageIndex > 0) {
currentImageIndex -= 1;
ShowImage();
}
}

// Shows the next Image:
function ShowNextImage() {
if (currentImageIndex < images.length - 1) {
currentImageIndex += 1;
ShowImage();
}
}

// Shows the last image:
function ShowLastImage() {
currentImageIndex = images.length - 1;
ShowImage();
}


Points to notice

  • A useful application built in a single web form and a folder with images

  • The EnableScriptGlobalization feature on the ScriptManager and the ability to extract language information in javascript using Sys.CultureInfo.CurrentCulture.name.



License

Copyright (c) 2008, Ole L. Sørensen
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

(The above license is based on the BSD license here: http://www.opensource.org/licenses/bsd-license.php)

No comments: