Slapping Together a Photo Gallery in ASP.NET Part II - My Gallery - Demystified
(Page 6 of 7 )
I always said that my "Photo Gallery" was going to be a simple, no-frills application. But, I never said that the programming that went into it was going to be a piece of cake.
As usual, let me break up code listing in smaller bits; that should definitely make things easier for both you and me.
First, I have the HTML code:
<HTML>
// snip
<!-- Panel for Photo Albums -->
<asp:Panel id="pnlPhotoAlbums" visible="false" runat="server" >
<asp:Placeholder id="pchPhotoAlbums" runat="server" />
</asp:Panel>
<P> </P>
<!-- Panel for Photos -->
<asp:Panel id="pnlPhotos" visible="false" runat="server" >
<asp:Placeholder id="pchPhotos" runat="server" />
</asp:Panel>
<P> </P>
<asp:HyperLink id="hypUpOneLevel" Text="Up One Level" visible="false" runat="server" />
// snip
</HTML>
There's nothing fancy here. I have created two panels; one contains a placeholder for the display of "Photo Albums," and the second one contains a placeholder for the display of "Photos" and one hyperlink control (not visible by default) that will allow the user to navigate "up one level" when browsing the "Photo Gallery."
Now, let me shift the focus to the programming section. At the onset, I've defined two variables:
<%
// snip
string strPhotoGalleryRootRelPath = "/Gallery";
int intItemsInRow = 3;
// snip
%>
The purpose of these variables is clearly documented in the code listing. The "strPhotoGalleryRootRelPath" string variable stores the root folder of the "Photo Gallery," relative to the folder where I have created the "gallery.aspx" script; and the "intItemsInRow" variable defines the number of photos or albums to be listed in a single row on the page.
Next, I have the Page_Load() function:
<%
// snip
void Page_Load(Object sender, EventArgs e) {
string strWebPath = "";
// check if relative path is sent in the Query String
if(Request.QueryString["strWebPath"] != null) {
strWebPath += Request.QueryString["strWebPath"];
} else {
// if not, then assign default value
strWebPath = strPhotoGalleryRootRelPath;
}
// start display of Photo Albums
GetPhotoAlbums(strWebPath);
}
// snip
%>
There is one little security flaw in all of the previous examples: the entire path (drive letter et. al.) to the file or folder was passed in the query string of the URL. This could serve as an open invitation to hackers to compromise the security of the server.
One alternative solution is to pass a relative path and use the Server.MapPath property to obtain the actual file system path, and that’s exactly what I've done. I've defined a string variable "strWebPath," that attempts to retrieve this relative path from the query string. If this isn't possible, then it defaults to the value stored in the "strPhotoGalleryRootRelPath" variable.
Next, I invoke my custom GetPhotoAlbums() function in order to display Photo Albums (followed by the Photos) in the folder specified in the variable "strWebPath." Time to review the updated GetPhotoAlbums() function.
<%
// snip
// custom function to display Photo Albums
void GetPhotoAlbums(string strWebPath) {
// counter to keep track of cells
int intCellCounter = 1;
// define array to store collection of Photo Albums
DirectoryInfo[] objPhotoAlbums;
// get absolute path from the relative web path
string strFSPath = Server.MapPath(strWebPath);
// get the details for current Photo Album i.e. folder
DirectoryInfo objCurrentAlbum = new DirectoryInfo(strFSPath);
// use the GetDirectories() method to obtain a list of sub-folders
objPhotoAlbums = objCurrentAlbum.GetDirectories();
// if there are Photo Albums i.e. sub-folders...
if(objPhotoAlbums.Length != 0) {
// make the "pnlAblum" Panel control visible
pnlPhotoAlbums.Visible = true;
// add new Table() to list the Photo Albums, neatly
Table objPhotoAlbumTable = new Table();
objPhotoAlbumTable.Width = 640;
objPhotoAlbumTable.BorderWidth = 3;
objPhotoAlbumTable.CellSpacing = 10;
objPhotoAlbumTable.CellPadding = 5;
objPhotoAlbumTable.HorizontalAlign = HorizontalAlign.Center;
// create TableRow() and Table Cell() object instances
TableRow objPhotoAlbumTableRow = new TableRow();
TableCell objPhotoAlbumTableCell = new TableCell();
// Add a header row along with cell for Photo Album table
objPhotoAlbumTableCell.ColumnSpan = intItemsInRow;
objPhotoAlbumTableCell.HorizontalAlign = HorizontalAlign.Center;
objPhotoAlbumTableCell.Controls.Add(new LiteralControl("<h3>Photo Albums</h3>"));
objPhotoAlbumTableRow.Cells.Add(objPhotoAlbumTableCell);
objPhotoAlbumTable.Rows.Add(objPhotoAlbumTableRow);
// create new row for Photo Album listings
objPhotoAlbumTableRow = new TableRow();
// iterate over collection of DirectoryInfo() objects
// to get details of each Photo Album i.e. folder
foreach(DirectoryInfo objPhotoAlbum in objPhotoAlbums) {
// three cells added to existing row
// create a new TableRow() object
if(intCellCounter > intItemsInRow) {
// add the TableRow() object to Table() ...
objPhotoAlbumTable.Rows.Add(objPhotoAlbumTableRow);
// ... create a new TableRow() object ...
objPhotoAlbumTableRow = new TableRow();
// and reinitialize cell counter
intCellCounter = 1;
} else {
// increment cell counter by 1
intCellCounter++;
}
// create Hyperlink() object - one for text hyperlink
// and another for a image hyperlink
HyperLink objAlbumTextLink = new HyperLink();
HyperLink objAlbumImageLink = new HyperLink();
objAlbumTextLink.NavigateUrl = objAlbumImageLink.NavigateUrl = "gallery.aspx?strWebPath=" + strWebPath + "/" + objPhotoAlbum.Name;
objAlbumTextLink.Text = objAlbumImageLink.Text = objPhotoAlbum.Name;
objAlbumImageLink.ImageUrl = "./folder.gif"; // link to a local image
// create a new TableCell() to display
// Photo Album hyperlinks in browser
objPhotoAlbumTableCell = new TableCell();
objPhotoAlbumTableCell.Width = 200;
objPhotoAlbumTableCell.BorderWidth = 1;
objPhotoAlbumTableCell.HorizontalAlign = HorizontalAlign.Center;
objPhotoAlbumTableCell.VerticalAlign = VerticalAlign.Middle;
// add Hyperlink() objects to TableCell()
objPhotoAlbumTableCell.Controls.Add(objAlbumImageLink);
objPhotoAlbumTableCell.Controls.Add(new LiteralControl("<BR /><BR />"));
objPhotoAlbumTableCell.Controls.Add(objAlbumTextLink);
// add TableCell() to TableRow()
objPhotoAlbumTableRow.Cells.Add(objPhotoAlbumTableCell);
}
// if cell counter is less than intItemsInRow
// then the layout needs to be padded with some empty
// table cells
if(intCellCounter <= intItemsInRow) {
objPhotoAlbumTableCell = new TableCell();
objPhotoAlbumTableCell.ColumnSpan = (intItemsInRow - intCellCounter + 1);
objPhotoAlbumTableCell.Controls.Add(new LiteralControl(" "));
objPhotoAlbumTableRow.Cells.Add(objPhotoAlbumTableCell);
}
// add the final row (with or without padded cells
objPhotoAlbumTable.Rows.Add(objPhotoAlbumTableRow);
// add the Table() to the Placeholder()
pchPhotoAlbums.Controls.Add(objPhotoAlbumTable);
}
// do not check for photos for Root folder
if(strWebPath != "" && strWebPath != strPhotoGalleryRootRelPath) {
// start display of Photos
GetPhotos(strFSPath, strWebPath);
// display "Up One Level" link
hypUpOneLevel.NavigateUrl = "gallery.aspx?strWebPath=" + strWebPath.Substring(0, strWebPath.LastIndexOf("/")) ;
hypUpOneLevel.Visible = true;
}
}
// snip
%>
Well, I'll take back my words: what I have above is not an "updated" version, but a totally revamped one!
Frankly, the majority of the code in this "revamped" version deals with the creation of Table(), TableRow() and TableCell() objects and their subsequent integration to render a listing of Photo Albums, as seen in the outputs listed in the previous section.
However, the underlying logic remains the same as before: create a DirectoryInfo() object, get a collection of DirectoryInfo() objects (one for each sub-folder) using the GetDirectories() method, iterate over the array and display information about each sub folder, along with a hyperlink to navigate down the hierarchy.
There is one more point that I would like to bring to your notice, which I've already spoken about earlier: the use of the Server.MapPath function. This allows us to convert the relative path, propagated in the query string, into the absolute file system path that is required by the DirectoryInfo() object.
Finally, I have the GetPhotos() function; this has been overhauled too. But, you will notice a striking similarity between the GetPhotoAlbums() and the GetPhotos() function. See for yourself:
<%
// snip
// custom function to display Photos
void GetPhotos(string strFSPath, string strWebPath) {
// counter to keep track of cells
int intCellCounter = 1;
// define array to store collection of Photos
FileInfo[] objPhotos;
// display name of current Photo Album
int intStartPosition = strFSPath.LastIndexOf("\\") + 1;
int intLength = strFSPath.Length - intStartPosition;
// make the Photos Panel visible
pnlPhotos.Visible = true;
// add new Table() to list the Photos, neatly
Table objPhotosTable = new Table();
objPhotosTable.Width = 640;
objPhotosTable.BorderWidth = 3;
objPhotosTable.CellSpacing = 10;
objPhotosTable.CellPadding = 5;
objPhotosTable.HorizontalAlign = HorizontalAlign.Center;
// create TableRow() and Table Cell() object instances
TableRow objPhotosTableRow = new TableRow();
TableCell objPhotosTableCell = new TableCell();
// Add a header row along with cell for Photos table
objPhotosTableCell.ColumnSpan = intItemsInRow;
objPhotosTableCell.HorizontalAlign = HorizontalAlign.Center;
objPhotosTableCell.Controls.Add(new LiteralControl("<h3>Photos in <U>" + strFSPath.Substring(intStartPosition, intLength) + "</U> Album</h3>"));
objPhotosTableRow.Cells.Add(objPhotosTableCell);
objPhotosTable.Rows.Add(objPhotosTableRow);
// instantiate DirectoryInfo() object to retrieve
// Photos i.e. image files present in Photo Album i.e. folder
DirectoryInfo objCurrentAlbum = new DirectoryInfo(strFSPath);
// Retrieve photos i.e. JPEG image files ONLY
objPhotos = objCurrentAlbum.GetFiles("*.jpg");
// create new row for Photo Album listings
objPhotosTableRow = new TableRow();
// check if there are any Photos i.e. image files in current Photo Album
if(objPhotos.Length != 0) {
// if there are Photos i.e. image files
foreach(FileInfo objPhoto in objPhotos) {
// three cells added to existing row
// create a new TableRow() object
if(intCellCounter > intItemsInRow) {
// add the TableRow() object to Table() ...
objPhotosTable.Rows.Add(objPhotosTableRow);
// ... create a new TableRow() object ...
objPhotosTableRow = new TableRow();
// and reinitialize cell counter
intCellCounter = 1;
} else {
// increment cell counter by 1
intCellCounter++;
}
// create new Image() object for each Photo
Image objImage = new Image();
objImage.ImageUrl = strWebPath + "/" + objPhoto.Name;
objImage.Width = 150;
objImage.Height = 115;
// create a new TableCell() to display
// Photo Album hyperlinks in browser
objPhotosTableCell = new TableCell();
objPhotosTableCell.BorderWidth = 1;
objPhotosTableCell.Width = 200;
objPhotosTableCell.HorizontalAlign = HorizontalAlign.Center;
objPhotosTableCell.VerticalAlign = VerticalAlign.Middle;
// add Hyperlink() objects to TableCell()
objPhotosTableCell.Controls.Add(objImage);
objPhotosTableCell.Controls.Add(new LiteralControl("<BR /><BR />"));
objPhotosTableCell.Controls.Add(new LiteralControl(objPhoto.Name));
// add TableCell() to TableRow()
objPhotosTableRow.Cells.Add(objPhotosTableCell);
}
} else {
objPhotosTableCell.ColumnSpan = intItemsInRow;
objPhotosTableCell.HorizontalAlign = HorizontalAlign.Center;
objPhotosTableCell.Controls.Add(new LiteralControl("Sorry, there are no photos in this Photo Album."));
objPhotosTableRow.Cells.Add(objPhotosTableCell);
objPhotosTable.Rows.Add(objPhotosTableRow);
}
// if cell counter is less than intItemsInRow
// then the layout needs to be padded with some empty
// table cells
if(intCellCounter <= intItemsInRow) {
objPhotosTableCell = new TableCell();
objPhotosTableCell.ColumnSpan = (4 - intCellCounter);
objPhotosTableCell.Controls.Add(new LiteralControl(" "));
objPhotosTableRow.Cells.Add(objPhotosTableCell);
}
// add the final row (with or without padded cells
objPhotosTable.Rows.Add(objPhotosTableRow);
// add the Table() to the Placeholder()
pchPhotos.Controls.Add(objPhotosTable);
}
// snip
%>
Things are no different, as is evident from the repeated appearance of the Table(), TableRow() and TableCell() objects. As before, the programming logic remains the same: I retrieve a collection of FileInfo() object using the GetFiles() method of the parent DirectoryInfo() object. I instantiate a new TableCell() object for each image file and insert it into the TableRows() and subsequently, the Table() object. Once it has been created in memory, I've added the Table() object to the "pchPhotos" server control, and the Photos are eventually rendered it in the browser.
Whew! That's about it as far as my "Photo Gallery" application is concerned.
The effort: programming 300 odd lines of code. The cost: loads of toil and sweat. The result: a neat little "Photo Gallery" that should bring a smile to every visitor’s face. The profit: priceless.
Next: Conclusion >>
More ASP.NET Articles
More By Harish Kamath