Photos Image Control Version 2

Thursday, 24 April 2008 16:31 by Frimbob

Earlier I created a custom User control , to fill a gab in the blog framework I am currently using. This user control read a assigned directory for subdirectories containing image files (JPG). This control would print category out onto the one screen.

While this was useful for a small number of images it obviously is not sufficient for future use. I learnt a lot about user controls then and applied this new knowledge in creating version 2

new controls ouput
Figure 1 Example of Version 2

Version 2 needed another layer,as you can see in the above image. Each folder is a category and I decided that I would not support subcategories of categories, this meant that I only required an extra menu.

Other Requirements

  • An option would be needed to set the number of Images per row.
  • CSS defaults would be Necessary as well as an option to turn them off
  • CSS Classes would need to be defined if defaults where turn off
  • Use the existing thumbnail httphandler.
  • Cross browser CSS consistency (lacking from version 1)
  • Written in C# (Version one was in VB)
  • Turn off the initial Category view off and just print every photo found.
  • Use the light box extension included with the blog framework
File Download Here

PhotoGallery.zip (6.27 kb)

Instructions

This control was developed for BlogEngine.NET  however it should work on any ASP.net 2.0 page this control also uses a httpHandler to create image thumbnails also included in the download. Currently only supports JPG file extensions.

Unzip the files to the "Your_blog_root"/User controls/..  folder.

The control can be implemented using BlogEngine user-control injection feature.

  1. Create a new page or post open end mode on a new line type [ ]
  2. type the following between the two brackets[]- usercontrol: ~/User controls/Photo2.ascx; PhotoDirectory=~/Photos/; NumberPerRow=3; NumberCatPerRow=2; ShowCategories=True;
  3. Ensure that no html is present between the [] tags.
  4. Each proeprty should end in';'.

There are 2 views both are shown below, the view on the left Will be referred to as the ' Category View'. The screen shot on the right will be referred to as the 'Item View' .

imageblog_240408 blogimage2_240408 

Supported Properties

  1. PhotoDirectory : path in string to the root folder where the images are stored default is "~/Photos/"
  2. NumberPerRow: Number of Images per row in the Item View e.g 5.
  3. NumberCatPerRow: Number of Images per row in the Category View e.g 2.
  4. ShowCategories: Bool (True/False) , Turn on or off Category View.
  5. CSSDefaults: Use the built in CSS Defaults.
  6. CatCellPadding: Category View cell padding, cant be set by css class.
  7. CatCellSpacing: Category View cell Spacing, cant be set by css class.
  8. ItemCellPadding: Item View cell padding, cant be set by css class.
  9. ItemCellSpacing: Item View cell Spacing, cant be set by css class. 

CSS Classes

CatagoryView

  • PhotosTable - Table element;
  • photoCatagoryLabel - Label Element inside the cell
  • photoCatagoryLabelImage - image element inside the cell
  • PhotosTableRow   - Row Element inside each row.

Item View

  • ItemTableLabel -Label at the top of the Item View
  • ItemTable - Table element
  • Itemhyp - hyperlink which the image is a child of
  • ItemCellImage - cell image.
  • ItemNewbtn - return to previous view button

Code Critique

This section is for other developer and potential employers 'wink wink'. Below I will present the code I created for this control and attempt to justify its existence. this will following in a class breakdown, then my method.

Why use this control? I wanted to spend a little time answering this one question, While there exist others for BlogEngine.Net , this control is less high tech than other controls that use Silver-Light or a re Java-Script heavy. This control should be considered to be the minimum that should be provided in the absence of scripting. Silver-Light, Flash and AJAX photo gallery's are simply just better looking but there always is a person who cant get the plusgins or have script access.

This control while having a script element (lightbox) if  turned off it will still achieve its aim of displaying images, and creating folders full of jpg's is quick and easy with any ftp tool.

Class Layout

This control consists of 3 Classes

  1. Class aphotoCatagory - this represent a single category and is instanced multiple times in a collection.
  2. Class PhotoData - this class is a wrapper for a collection of aphotoCatagory objects, it also handles creation of this collection through File IO interaction.
  3. Class User_controls_Photo2 -  this class contains the display methods and event handlers.

In essence we have object which represent a folder another object that represents a collection of folders and an object to represent the User Interface.  Its a rather group of objects

Can we have multiple user controls on a single page and the answer is Yes each user control is and individual namespaces that is not accessible through normal means to the container page. You can think off classing wrapped in a User Control as private unless changed. 

aphotoCatagory

   1: public class aphotoCatagory
   2: {
   3:     #region declarations
   4:     //catagory name
   5:     public String Cname;
   6:     public String Cpath;
   7:     #endregion
   8:  
   9:     #region Private
  10:     private List<string> Cpaths = new List<string>();
  11:     #endregion
  12:  
  13:     #region  Public
  14:     public bool Add(string str)
  15:     {
  16:         Cpaths.Add(str);
  17:         if (Cpaths.Contains(str))
  18:         {
  19:             return true;
  20:         }
  21:         else
  22:         {
  23:             return false;
  24:         }
  25:  
  26:     } //end of add
  27:  
  28:     public bool remove(string str)
  29:     {
  30:  
  31:         if (Cpaths.Remove(str))
  32:         {
  33:             return true;
  34:         }
  35:         else
  36:         {
  37:             return false;
  38:         }
  39:  
  40:     }//end of remove
  41:  
  42:     public int count()
  43:     {
  44:         return Cpaths.Count;
  45:  
  46:     }//end of cound
  47:  
  48:     public string getItem(int item)
  49:     {
  50:         if (item < Cpaths.Count && item >= 0)
  51:         {
  52:             return Cpaths[item];
  53:  
  54:         }
  55:         else
  56:         {
  57:             return "No item found at this index";
  58:         }
  59:  
  60:     } //end of getitem
  61:     #endregion
  62:  
  63: }//end if aphotoCatagory

The methods are very simple there is a Count() to number of files found, getItem() which return the path of a file , and add() and remove() methods to manipulate the files in the collection. This class has 2 properties a Name and a Cpath which is unused at the current time.

Class PhotoData

   1: public class PhotoData
   2: {
   3:     #region Declarations and properties
   4:  
   5:     //the directory that holds the photos
   6:     private List<aphotoCatagory> CatagoryLists = new List<aphotoCatagory>();
   7:  
   8:     private String _PhotoDirectory;
   9:     #endregion
  10:  
  11:     #region Private Class Methods
  12:     private void makeCatagoryList(string PhotoDirectory)
  13:     {
  14:         try
  15:         {
  16:             if (Directory.Exists(HttpContext.Current.Server.MapPath(PhotoDirectory)))
  17:             {
  18:                 string[] aryDirectories = 
  19:                 Directory.GetDirectories(HttpContext.Current.Server.MapPath(PhotoDirectory));
  20:  
  21:                 string[] files;
  22:                 string[] ExclusionDirectories = { "_vti_cnf", "_vti_pvt", "_vti_script" };
  23:  
  24:                 //test for front page extensions and remove them
  25:                 for (int i = 0; i < aryDirectories.Length; i++)
  26:                 {
  27:                     //test for folder with _vt_cnf , _vt_pvt _vti_script
  28:                     foreach (string j in ExclusionDirectories)
  29:                     {
  30:  
  31:                         if (aryDirectories[i].Contains(j))
  32:                         {
  33:                             aryDirectories[i] = "";
  34:                         } //end if
  35:                     } //end for each
  36:                 } //end for
  37:                 int ab = aryDirectories.Length;
  38:  
  39:                 //test for condition that no subfolders found
  40:                 if (aryDirectories.Length == 1 && aryDirectories[0] == "")
  41:                 {
  42:                     files = Directory.GetFiles(HttpContext.Current.Server.MapPath(PhotoDirectory));
  43:                     if (files.Length >= 1) //if not empty
  44:                     {
  45:                         aphotoCatagory cat = new aphotoCatagory();
  46:                         string keyname = returnCatagoryName(HttpContext.Current.Server.MapPath(PhotoDirectory));
  47:                         cat.Cname = keyname;
  48:                         cat.Cpath = HttpContext.Current.Server.MapPath(PhotoDirectory);
  49:                         PhotoItems(keyname, ref cat, false); // fill the object up
  50:                         CatagoryLists.Add(cat); //add the catagory to the list;   
  51:                     } //end if  
  52:  
  53:                 }
  54:                 else
  55:                 {
  56:                     for (int i = 0; i < aryDirectories.Length; i++)
  57:                     {
  58:                         files = Directory.GetFiles(aryDirectories[i]); //test for empty directories
  59:                         if (files.Length >= 1) //if not empty
  60:                         {
  61:                             aphotoCatagory cat = new aphotoCatagory();
  62:                             string keyname = returnCatagoryName(aryDirectories[i]);
  63:                             cat.Cname = keyname;
  64:                             cat.Cpath = aryDirectories[i];
  65:                             PhotoItems(keyname, ref cat, true); // fill the object up
  66:                             CatagoryLists.Add(cat); //add the catagory to the list;   
  67:                         } //end if
  68:                     }//end for
  69:                 }//end if
  70:  
  71:             }//end if
  72:         }
  73:         catch
  74:         {
  75:             //just produce null wont crash the Web application
  76:         }
  77:     } //end of makeCatagoryList()
  78:  
  79:     private void PhotoItems(string key, ref aphotoCatagory cat, Boolean subs)
  80:     {
  81:         string[] aryDirectories = null;
  82:         string keys;
  83:  
  84:         //combine the relative path and the key Catagory Name: 'Play'
  85:         if (subs)
  86:         {
  87:             keys = Path.Combine(_PhotoDirectory, key);
  88:  
  89:             //get the files in the directory returns c:\\Directory\\Directory\\Directory\\Directory\\FileName.jpg'
  90:             //use mappath change our relative path to application root.
  91:             aryDirectories = Directory.GetFiles(HttpContext.Current.Server.MapPath(keys), "*.jpg");
  92:  
  93:             //make the path relative not the full c:\\Directory\\Directory\\Directory\\Directory\\FileName.jpg'
  94:             for (int i = 0; i < aryDirectories.Length; i++)
  95:             {
  96:                 //return a file name from the full path e.g 'image01.jpg'
  97:                 string str = Path.GetFileName(aryDirectories[i]);
  98:                 //combine our relative path and with the catagory name.
  99:                 string temp = Path.Combine(_PhotoDirectory, this.returnCatagoryName(aryDirectories[i]));
 100:                 //combine the filename and the relative path (cant use path.combine)
 101:                 aryDirectories[i] = temp + '/' + str;
 102:                 cat.Add(aryDirectories[i]);
 103:             } //end for
 104:         }
 105:         else
 106:         {
 107:             keys = _PhotoDirectory;
 108:  
 109:             //get the files in the directory returns 
 110:             //c:\\Directory\\Directory\\Directory\\Directory\\FileName.jpg'
 111:             //use mappath change our relative path to application root.
 112:             aryDirectories = Directory.GetFiles(HttpContext.Current.Server.MapPath(keys), "*.jpg");
 113:  
 114:             //make the path relative not the full 
 115:             //c:\\Directory\\Directory\\Directory\\Directory\\FileName.jpg'
 116:             for (int i = 0; i < aryDirectories.Length; i++)
 117:             {
 118:                 string str = Path.GetFileName(aryDirectories[i]);
 119:                 aryDirectories[i] = Path.Combine( _PhotoDirectory , str); 
 120:                 cat.Add(aryDirectories[i]);
 121:             } //end fir
 122:         
 123:         }//end if
 124:         
 125:  
 126:     } //end of PhototItrms
 127:  
 128:     #endregion
 129:  
 130:     #region public Methods
 131:     public int count()
 132:     {
 133:         return CatagoryLists.Count;
 134:     }
 135:  
 136:     public string returnCatagoryName(string dpath)
 137:     {
 138:  
 139:         string[] nameary; //temp var
 140:         nameary = dpath.Split(Path.DirectorySeparatorChar);
 141:         string CatName = null;
 142:         
 143:         if (Path.HasExtension(dpath))
 144:         {
 145:             CatName = nameary[nameary.GetUpperBound(0) - 1];
 146:         }//end if
 147:         else
 148:         {
 149:             CatName = nameary[nameary.GetUpperBound(0)];
 150:         }//end else
 151:         return CatName;
 152:     } //end of returnCatagoryName
 153:  
 154:  
 155:     public aphotoCatagory getItem(int item)
 156:     {
 157:         if (item < CatagoryLists.Count && item >= 0)
 158:         {
 159:             return CatagoryLists[item];
 160:  
 161:         }
 162:         else
 163:         {
 164:             return null;
 165:         }
 166:  
 167:     } //end of getitem
 168:  
 169:     #endregion
 170:  
 171:     #region constructor
 172:     public PhotoData(string directory)
 173:     {
 174:         _PhotoDirectory = directory; //set the state variable
 175:         makeCatagoryList(_PhotoDirectory);  //fill the dictionary object
 176:  
 177:     }
 178:     #endregion
 179:  
 180: } //end of class

You will notice that each category is stored into a List<aphotoCatagory> objected call CatagoryLists. The other methods in the class are concerned with scanning directories for images an building the datastructure.

You may notice the following code:-

   1: private void makeCatagoryList(string PhotoDirectory)
   2:     {
   3:         //code remove for bevity
   4:         
   5:     string[] ExclusionDirectories = { "_vti_cnf", "_vti_pvt", "_vti_script" };
   6:  
   7:                 //test for front page extensions and remove them
   8:                 for (int i = 0; i < aryDirectories.Length; i++)
   9:                 {
  10:                     //test for folder with _vt_cnf , _vt_pvt _vti_script
  11:                     foreach (string j in ExclusionDirectories)
  12:                     {
  13:  
  14:                         if (aryDirectories[i].Contains(j))
  15:                         {
  16:                             aryDirectories[i] = "";
  17:                         } //end if
  18:                     } //end for each
  19:                 } //end for
  20:            //code removed for bevity
  21:      }
  22:     //end class

When I was developing this extension, I wanted to be able to specify a sing directory e.g instead of the root folder ' ~/photos '  I wanted to specify '~/photos/play' an have my code produce just that directory. What I found was that when Front-Page extensions are installed runtime directories are created on the web-server called _vti_cnf , amongst others. this meant that my function would return this directory instead of a value of  0. The above code removes these directories by setting their value as an empty string.  Which can be later test for in the below.

   1: if (aryDirectories.Length == 1 && aryDirectories[0] == "")
   2: {
   3:  //deep folder with Frontpage extensions installed   
   4: }    
   5: else if(aryDirectories.Length ==0)
   6: {
   7: //deep folder with no Frontpage extensions installed                 
   8: }
   9: else
  10: {
  11:  //normal root folder                    
  12: }//end if

Class User_controls_Photo2

   1: public partial class User_controls_Photo2 : System.Web.UI.UserControl
   2: {
   3:  
   4:     #region Delecrations
   5:     private PhotoData Photo;
   6:     #endregion
   7:  
   8:     #region Properties
   9:     private string PD = "~/Photos/play";
  10:     public string PhotoDirectory
  11:     {
  12:         get
  13:         { return PD; }
  14:         set
  15:         { PD = value; }
  16:  
  17:     }
  18:  
  19:     //Number of items per row
  20:     private int numberperrow = 3;
  21:     public int NumberPerRow
  22:     {
  23:         get
  24:         { return numberperrow; }
  25:         set
  26:         { numberperrow = value; }
  27:  
  28:     }
  29:  
  30:     //Number of Cat items per row
  31:     private int numbercatperrow = 3;
  32:     public int NumberCatPerRow
  33:     {
  34:         get
  35:         { return numbercatperrow; }
  36:         set
  37:         { numbercatperrow = value; }
  38:  
  39:     }
  40:  
  41:     //show catagoires?
  42:     private bool SC = true;
  43:     public bool ShowCategories
  44:     {
  45:         get
  46:         { return SC; }
  47:         set
  48:         { SC = value; }
  49:  
  50:     }
  51:  
  52:     //CSSstyledefaults
  53:     private bool CSSDef = true;
  54:     public bool CSSDefaults
  55:     {
  56:         get
  57:         { return CSSDef; }
  58:         set
  59:         { CSSDef = value; }
  60:  
  61:     }
  62:  
  63:  
  64:     //Cell padding Cat
  65:     private int CatCP = 0;
  66:</