|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article demonstrates a photo gallery template, similar to the Windows XP filmstrip folder view with Slideshow features and "on-fly" thumbnail image generation during run-time. As an option there is a demonstration of how to use the basic BackgroundThere are many photo galleries available throughout the web, but to my surprise, I wasn't able to find what I needed exactly, for a small project. Most of the photo albums show a thumbnail gallery similar to the "Icon" view, which usually takes a lot of screen space when you have a number of images. Anyway, my client had asked to show images as a thumbnail "strip" with scrolling through them and having large images with some details below, and it gave me a reason to start developing this. And in my opinion, this type of a gallery suits well, for example, to show a catalog of items, etc. The main idea here is to show small images from the gallery in one row with scrolling back and forth, and put one large image below, something similar to the WinXP folder “Filmview”, or similar to a catalog view. When a user clicks on a small image, he gets a large image and some details about the product, item or photo, that is pulled from a database or a data file. (It is a different story to link data with images.) Using the codeThe page has the following controls: a <add key="photofolder" value="photo"/>
When the page is loaded, the So, we have our folder list from the main photo directory. What is next? We want to show only portions, for instance, 10 images, and indicate that there is more to show. I used a Now, we can bind the data and use the ViewState to keep track of pages, etc. If there is only one page, the navigation control could be hidden or disabled, depending on client preference. I put some navigation control here like "Go to Page: #", "Go to last/first page" etc. The This method will iterate through the chosen directory (depending on the parameter ?G=""), and using the Within the <a><img id=iPhoto runat=server
src='<%# "ThumbGen.ashx?path="+Container.DataItem%>'
title='<%# Container.DataItem %>'
width="69" height="90" style="cursor:hand"
class=photoover>
</a>private void GetPhotos()
{
string photodir =
System.Configuration.ConfigurationSettings.AppSettings["photofolder"];
//or get from config file
string curGallery="";
// use convention G is parameters to switch between galleries
if (Request["G"]!=null)
{
curGallery=Request["G"]; //find where we are
}
else
{
curGallery=
System.IO.Directory.GetDirectories(Server.MapPath(photodir))[0];
//if no parameters show first directory
string parent=(string) ViewState["parent"];
curGallery=curGallery.Substring(parent.Length);
}
// try get all files in folder
try
{
//get thumbnails and photos arrays in current directory
string[] photos = System.IO.Directory.GetFiles(
Server.MapPath(photodir+"/"+curGallery),"*.jpg");
// this array used if we have separate files as
// thumbnail images. Convention here to add _thumb to file name
//string[] thumb=System.IO.Directory.GetFiles(
// Server.MapPath("photo/"+curGallery),"*_thumb*");
//get directory info itself
int i=System.IO.Directory.GetParent(
Server.MapPath(photodir)).FullName.Length;
//
//get files names/ relative path
//just get actual files names for thumbnails and photos
//just removing extra path, leaving only catalog and file name
for (int ix=0; ix<photos.Length; ix++)
{
photos[ix]=photos[ix].Substring(i+1);
}
//ArrayList gallery=new ArrayList(photos);
//find how many photos and how many pages to create pages
int num=photos.Length;//it is number of photos
//int num=thumb.Length; old usage
lblPage.InnerText=num.ToString();
if (num>9) imgbNext.Visible=true;
//here we are using paged datasource
PagedDataSource objPds = new PagedDataSource();
//
objPds.DataSource = photos; //we will show thumbnails
// Indicate that the data should be paged
objPds.AllowPaging = true;
// Set the number of items you wish to display per page
objPds.PageSize = 10;
//fixed page size, other option
//to control through drop down box.
objPds.CurrentPageIndex = CurrentPage;
ViewState["Pages"]=objPds.PageCount;
//fill pages values for dropdown list
//do it only first time
if (!Page.IsPostBack)
{
dlPages.Items.Clear();
for (int ip=1;ip<=objPds.PageCount;ip++)
{
dlPages.Items.Add(ip.ToString());
}
}
else
{
//list already there, so just set the right page
//dlPages.SelectedIndex=CurrentPage;
}
lblPage.InnerText=" Page "+(CurrentPage+1).ToString() +
" of "+objPds.PageCount.ToString();
//control visibility of next/prev buttons
imgbPrev.Enabled = !objPds.IsFirstPage;
//imgbPrev.Visible=!objPds.IsFirstPage;
imgbNext.Enabled = !objPds.IsLastPage;
//imgbNext.Visible = !objPds.IsLastPage;
repPhotoG.DataSource=objPds;
//
//DIV1.InnerText=thumb.
//gallery.Add()
repPhotoG.DataBind();
//we also will use item data bind events
//to add some links to thumbnails
}
catch (Exception ex)
{
//do something with error
}
//imgLarge.Attributes["onfilterchange"]="fade()";
}
Initially, I created separate thumbnail images with some distinctive names and then parsed file names to select only thumbnails, but not large images from the directory, and bound the list with my public class ThumbGen : IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
if (context.Request["path"]!=null)
//we have a pth try process image
{
string img=(string)context.Request["path"];
//here is path for application dir
string path1=context.Server.MapPath(context.Request.ApplicationPath);
path1=context.Server.MapPath(img);
//img=path1+"\\"+img; //full path to image
img=path1;
System.Drawing.Image.GetThumbnailImageAbort myCallback =
new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
//get bitmap from file
Bitmap bitmap = new Bitmap(img);
// process image, create thumbnail.
// Try use to reduce size, instead of GetThubmnail
ImageFormat imgformat=bitmap.RawFormat;
//we will use fixed size for picture, in our case 69x90
//but easy to add parameters such as width and calculate height
/////////////////this code borrowed from Rick Strahl,
// http://west-wind.com/weblog
//works better than NET method
//GetThumbnail() when need transparent gif
//Bitmap bmpOut=new Bitmap(70,90);
//Graphics g=Graphics.FromImage(bmpOut);
//g.InterpolationMode=System.Drawing.Drawing2D.
// InterpolationMode.HighQualityBicubic;
//g.FillRectangle(Brushes.White,0,0,69,90);
//g.DrawImage(bitmap,0,0,69,90);
///////////////////////////////////////////////////
// ALternatively you can use
System.Drawing.Image imgOut=
bitmap.GetThumbnailImage(70, 90, myCallback, IntPtr.Zero);
bitmap.Dispose();
//but seems creating Black background for transparent GIF's
//////////////////return our image back to stream
context.Response.ContentType="image/jpeg";
imgOut.Save(context.Response.OutputStream, ImageFormat.Jpeg);
//bmpOut.Save(context.Response.OutputStream, ImageFormat.Jpeg);
//bmpOut.Dispose();
imgOut.Dispose();
}
I left commented code, that I used as well, so you could try different ways to generate thumbnails. I used a standard method available in the .NET framework. Another approach creates a white background first and then loads the bitmap on it. At the same time I use hard coded thumbnail image sizes, but again it could be passed as optional parameters to the handler or be calculated based on the image orientation (landscape or portrait). It is possible, for example, to add more parameters, like size, width or height for the thumbnail image etc. In my case, landscape images will look distorted. Finally each small image will be generated "on-the-fly" and returned back to the page. In addition, there are some Microsoft filters for thumbnails and large photos. All thumbnail photos are shown in gray and becomes colored on mouse hover. Large images have a fade effect when they are changed. To avoid "flickering", the page transition effect is applied to the whole page, but it be might more appropriate to apply the transition to the thumbnail only. (Place to experiment.) After we have got the thumbnail gallery, we still need to decide how to show large images. For this I am using the img.Attributes["onclick"]="showImg('"+IMGpath(img.Src)+"')";
Here is the JavaScript function: function showImg(imgName) {
imgOn = ("" + imgName);
document.imgLarge.src = imgOn;
hdr.innerHTML=imgName; document.imgLarge.filters[0].Apply();
document.imgLarge.filters[0].Play();
}
So, when the user clicks on a small image, the large image At the same time, I decided to check the size of the first image and use this info to change the size of my large image to have a proportional picture. Initially, I had all my images in all the folders the same size, that was good just for me, but in real life, images could be of different sizes and orientations. In my case they should be the same size within one folder only. May be it is not the best way so far, somehow it is better to resize each image on the client side before assigning it for a large one. For this purpose (to find the size of images), I used a private void repPhotoG_ItemDataBound(object sender,
System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
//finding repeater item Image
System.Web.UI.HtmlControls.HtmlImage img=
(System.Web.UI.HtmlControls.HtmlImage)e.Item.FindControl("iPhoto");
//create a relative path for image and add onclick function
img.Src=img.Src.Replace('\\', '/');
img.Attributes["onclick"]="showImg('"+IMGpath(img.Src)+"')";
//some visual effects using MS filters
img.Attributes["onmouseover"]="this.className='thumb'";
img.Attributes["title"]=img.Src;
img.Attributes["onmouseout"]="this.className='photoover'";
//here we are trying to get size of images
//within directiry to set a more proportional
//thumbnail. The rule to have all images within folder the same size.
if (e.Item.ItemIndex==0)
{
imgLarge.ImageUrl=IMGpath(img.Src); //show first photo in folder
string path=Server.MapPath(imgLarge.ImageUrl);
Bitmap newimg = new Bitmap(path);
double ratio= (double)newimg.Width/(double)newimg.Height;
lblName.InnerText="size:"+newimg.Width.ToString()
+"-"+newimg.Height.ToString();
lblName.InnerText=lblName.InnerText+"Resolution:"+
newimg.VerticalResolution.ToString();
imgLarge.Style["Width"]=(imgLarge.Width.Value*ratio).ToString();
newimg.Dispose();
}
}
That's it. To use this gallery in real life just specify the main folder name, put PhotGallery.aspx on the web server, copy the bin folder, and create a subfolder with images, and don't forget to correct the Web.config file (add config keys and the Updates (October 24,2005):A slideshow feature is added. Again, perhaps there are a few different ways to create a slideshow. My intention was not to use any postback and do everything on client side. The idea behind slideshows is to have a list of images on the client side and then dynamically change our For this purpose, I put a hidden How it works: clicking on Slideshow-Launch will activate the slideshow and will show the "slide control box" on the top of the image. (Previous/Next/Pause/ Full Size/Stop) For "Fullsize" functionality, we are using DTHML so we can change the layout and the position of elements. So when the user will click on the "FullSize" button, he will get an image on the middle (the position will be calculated using the client screen size). To stop the slide show, for simplicity sake, I just used a regular ASP.NET button "postback", which will refresh a page and reload the gallery again. Otherwise we need to have a function opposite to "Full-Size" and return everything back. Yet another option could be a separate "full screen" window. ////////////////
//start slideshow, using list of images from list box
function SlideShow()
{
document.imgLarge.filters[0].Duration=2.5;
document.imgLarge.filters[0].Apply();
//alert(document.getElementById("listImg").options[i].text);
document.getElementById('hdr').innerText=
document.getElementById("listImg").options[i].text;
document.imgLarge.src=document.getElementById("listImg").options[i].text;
document.imgLarge.filters[0].Play();
if (i>=((document.getElementById("listImg").length)-1))
{
i=0;
}
else
{
i=i+1;
}
}
"AJAX" way to create a slideshow: Just to explore the AJAX technology the new option is added to I tried this way just to see for myself how we can use the I believe there is a lot of room for improvement, but this just one template to build a gallery and a different example of Javascripts/DHTML, and the remote callback technology. function ProcessResponse()
{
if(req.readyState == 4)
{
if(req.status == 200) //analog "OK",
{
//here we are processing result,////////////////////////
//
var p=req.responseXML.documentElement;
///alert(req.responseXML); -testing purpose
var ddlTest = document.getElementById("test");
//var limg=document.getElementById("listImg");
for (var count = ddlTest.options.length-1;count >-1; count--)
{
ddlTest.options[count] = null;
}
var photos = p.getElementsByTagName('photos');
var text;
//here we are trying to create array in memory
//this example just to show how we can
//fill selection box from callback result.
//After callback we got XMl document-table
//and we can use it to fill table/select/list etc.
var listItem;
var l=photos.length;
for (var count = 0; count < photos.length; count++)
{
text = (photos[count].textContent ||
photos[count].innerText || photos[count].text);
listItem = new Option(text, text, false, false);
ddlTest.options[ddlTest.length] = listItem;
}
}
else
{
alert("Error retrieving data!" );
}
////// this script responsible to fill Album array
// by data returned from client in our case
// it is a images pathes.
for (i = 0; i<l; i++)
{
text2 = (photos[i].textContent ||
photos[i].innerText || photos[i].text);
Album[i]= new Image();
Album[i].src = text2;
}
//now we have array with images. call slide show
slide=0;
//call slide show and show menu on top of image
AjaxSlideShow();
SlideMenu();
}function AjaxSlideShow()
{
StopSlide();
if (slide>=Album.length)
{
slide=0;
}
document.imgLarge.filters[0].Duration=2.5;
document.imgLarge.filters[0].Apply();
document.imgLarge.src=Album[slide].src;
document.imgLarge.filters[0].Play();
var tm=document.getElementById("slidetime").value*1000
oInterval=window.setTimeout("AjaxSlideShow()",tm);
//nr=nr+1;
slide++;
}
Points of InterestI am thinking of extending the handler by adding some methods to get images from a database and tie the images with a data file or a database. I am still looking to improve thumbnail loading to avoid "flickering" and get a more smooth appearance. I also left a link to the "Slideshow" function: to play all the files from the directory - that is to be implemented. Also I noticed that filters are not working in the first place when a page is just loaded and starts working after the next click. Also I want to try converting this to a User Control with some external properties. There is always a way to add something, to improve. That's why I like programming, it is endless. History
| ||||||||||||||||||||