Search - Articles - Dev Forums - Favorites - Member Login  
DevASP.NET for ASP.NET, VB.NET, XML and C# (C-Sharp) Developers Friday, September 03, 2010

Dev Articles
Search Directory
ASP.NET
VB.Net
C-Sharp
SQL Server
 


Displaying Additional Attributes


Just displaying the names of the child directories and files is not enough. We want to display much more information: an icon to describe the type of item, the attributes, size, the date of creation, and date of last modification. The first thing to do is to create new columns in the FoldersAndFiles table in BrowseFiles.aspx:

 

<asp:Table ID="FoldersAndFiles" runat="server"

    CssClass="Grid_General" Width="100%">

  <asp:TableRow CssClass="Grid_Header">

    <asp:TableCell Width="36px" />

    <asp:TableCell Text="Index" />

    <asp:TableCell Text="Attribs" Width="50px" />

    <asp:TableCell Text="Size" Width="80px" HorizontalAlign="Right" />

    <asp:TableCell Text="Created" Width="140px" />

    <asp:TableCell Text="Last Modified" Width="140px" />

  </asp:TableRow>

</asp:Table>


We also need to declare new cells and controls in the FillFoldersAndFilesTable procedure in the code-behind, BrowseFiles.aspx.cs:

 

TableRow rowItem;

TableCell cellItemLink;

HyperLink linkItem;

TableCell cellItemIcon;

TableCell cellSize;

TableCell cellAttributes;

TableCell cellCreated;

TableCell cellLastModified;

Label labelAttributes;

System.Web.UI.WebControls.Image imgItemIcon;

 

Now we have to actually create, set, and add these new cells for each directory and file shown in the table. We're going to present the code required for the new columns, one column at a time.

Displaying the Item Icon

Creating the icon for the subdirectories and the link to the parent directory is straightforward, since we use a fixed icon. We just create the Image control named imgItemIcon, set its ImageUrl property, and add it to the first cell. Here's the code for the parent link block, in BrowseFiles.aspx.cs:

 

if (folderPath != "/")

{

  rowItem = new TableRow();

  cellItemLink = new TableCell();

  linkItem = new HyperLink();

  cellItemIcon = new TableCell();

  imgItemIcon = new System.Web.UI.WebControls.Image();

 

  // create the cell with the FolderUp icon

  imgItemIcon.ImageUrl = "./Images/ParentFolder.gif";

  cellItemIcon.Controls.Add(imgItemIcon);

  cellItemIcon.HorizontalAlign = HorizontalAlign.Right;

 

  // add the link that points to the parent directory

  ...

 

  // add the cells to the new row

  rowItem.Cells.Add(cellItemIcon);

  rowItem.Cells.Add(cellItemLink);

  

  // add the row to the table

  rowItem.ApplyStyle(styleFolderRow);

  FoldersAndFiles.Rows.Add(rowItem);

}


The code for each child directory is very similar:

 

foreach (DirectoryInfo childDir in childDirs)

{

 

  // create the required cells and controls

  rowItem = new TableRow();

  cellItemLink = new TableCell();

  linkItem = new HyperLink();

  cellItemIcon = new TableCell();

  imgItemIcon = new System.Web.UI.WebControls.Image();

           

  // add the icon with the closed folder

  imgItemIcon.ImageUrl = "./Images/ClosedFolder.gif";

  cellItemIcon.Controls.Add(imgItemIcon);

  cellItemIcon.HorizontalAlign = HorizontalAlign.Right;

   

  // create the link that points to this sub-directory

  ...

   

  // add the cells to the new row, and add the row to the table

  ...

}

 

So far, so good. Now here's the more interesting part – adding an icon for each file. If we only wanted to differentiate files from folders, we could just associate a fixed icon for all the files (different from the one used for the folders). But we want to do something more. We want to display the icon that is usually shown in Windows Explorer to describe the file type. In Windows each file extension is identified by an icon, which is provided by the associated program. This icon is usually stored within a binary EXE or DLL file, and its index is stored in the registry. There are API functions to programmatically retrieve the icon index within a file, and then show the icon through the Windows ListView control, based on the icon's handle.

 

This is very tricky in ASP.NET, since the srcattribute of an <img> tag should point directly to an image file.

 

It is possible to extract the icon associated with the file extension from a binary file, save it to disk, and show it with the <img> tag, or even directly send a binary stream to the browser. However, this would require a lot of work, would slow down the execution, and would not bring about any valuable improvements to the application, so we decided not to cover this method here.

 

We will create a predefined set of fixed images for the most common files used for a website. These images will be assigned to the files based on their extension. If we name such images according to the file extensions (for example, aspx.gif, html.gif, ascx.gif, zip.gif, bmp.gif, gif.gif, etc.) we'll just need to check if there is an image with a name corresponding to the file extension, and if so show it. If there is no image with that name, we'll show a predefined image, unknown.gif. The file icons for many extensions are in the code download, but of course you can add your own.

 

To speed up the test that verifies the existence of a certain icon, we define an array of extensions for which we've created the appropriate image, and the check is done against this array. Here's how the array is defined, as a private variable for the BrowseFiles class:


 

private string[] extensions = new string[]{".aspx", ".html", ".ascx",  

  ".zip", ".bmp", ".gif", ...};

 

The rest of the file extensions have been left out here and you can add any others appropriate to your site's requirements. In the code download you'll find that 52 different extensions are already supported!

 

Thanks to the Array.IndexOf shared method (which returns the index of the element being searched for in the array), checking whether the extension of a file is present within the extensions array is a matter of a single line of code. Here is the updated block that adds all the child files to the table:

 

foreach (FileInfo childFile in childFiles)

{

 

  // create the required cells and controls

  rowItem = new TableRow();

  cellItemLink = new TableCell();

  linkItem = new HyperLink();

  cellItemIcon = new TableCell();

  imgItemIcon = new System.Web.UI.WebControls.Image();

  int extIndex;

   

  // create and add the link that points to this file in a new window

  ...

 

  // search for the extension in the array,

  // and if it's found show the respective icon

  // if not found add a general icon for unknown files

  extIndex = Array.IndexOf(extensions, childFile.Extension.ToLower());

  if (extIndex > -1)

    imgItemIcon.ImageUrl = "./Images/" +

      childFile.Extension.Substring(1) + ".gif";

  else

    imgItemIcon.ImageUrl = "./Images/unknown.gif";

 

  // add the icon to the first cell

  cellItemIcon.Controls.Add(imgItemIcon);

  cellItemIcon.HorizontalAlign = HorizontalAlign.Right;

 

  // add the cells to the new row

  rowItem.Cells.Add(cellItemIcon);

  rowItem.Cells.Add(cellItemLink);

 

  // add the new row to the table

  ...

}

Displaying the Item Attributes

Let's go ahead with the creation of the third column, the one that displays a list of attributes for the

subdirectories and files. The modifications for the block that adds the directories and the block that adds the files are exactly the same. For the first row, which points to the parent directory (if any), we just add an empty cell. This is shown in the following code:


 

if (folderPath != "/")

{

  ...

  // create the cell with the FolderUp icon

  // add the link that points to the parent directory

  ...

   

  // add the cells to the new row

  rowItem.Cells.Add(cellItemIcon);

  rowItem.Cells.Add(cellItemLink);

  rowItem.Cells.Add(new TableCell());

 

  // add the row to the table

  ...

}

 

foreach (DirectoryInfo childDir in childDirs)

{

 

  // create the required cells and controls

  ...

  cellAttributes = new TableCell();

  labelAttributes = new Label();

               

  // add the icon with the closed folder 

  ...

 

  // create the link that points to this sub-directory

  ...

   

  // set the description label to the Attributes cell      

  labelAttributes.Text = GetAttributesDescription(childDir.Attributes);

  labelAttributes.Font.Name = "Courier";

  cellAttributes.Controls.Add(labelAttributes);

 

  // add the cells to the new row and add the row to the table

  rowItem.Cells.Add(cellAttributes);

  ...

}

 

// now add each child file

foreach (FileInfo childFile in childFiles)

{

 

  // create the required cells and controls

  ...

  cellAttributes = new TableCell();

  labelAttributes = new Label();

   

  // create the icon and the link that points to this file

  ...

   

  // set the description label to the Attributes cell      


  labelAttributes.Text = GetAttributesDescription(childFile.Attributes);

  labelAttributes.Font.Name = "Courier";

  cellAttributes.Controls.Add(labelAttributes);

 

  // add the cells to the new row and add the row to the table

  rowItem.Cells.Add(cellAttributes);

  ...

}

 

As you can see, the string that describes the item's attributes is not built directly in the code above, but is returned by a custom procedure called GetAttributesDescriptions, which receives as input the value of the Attributes property, exposed by both the DirectoryInfo and FileInfo data type. Here is its code:

 

protected string GetAttributesDescription(FileAttributes attribs)

{

  string itemAttribs = "";

 

  // check if the Archive attribute is set

  if ((attribs & FileAttributes.Archive) == FileAttributes.Archive)

    itemAttribs += "A";

  else

    itemAttribs += "&nbsp;";

 

  // check if the ReadOnly attribute is set

  if ((attribs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)

    itemAttribs += "R";

  else

    itemAttribs += "&nbsp;";

 

  // check if the Hidden attribute is set

  if ((attribs & FileAttributes.Hidden) == FileAttributes.Hidden)

    itemAttribs += "H";

  else

    itemAttribs += "&nbsp;";

 

  // check if the System attribute is set

  if ((attribs & FileAttributes.System) == FileAttributes.System)

    itemAttribs += "S";

  else

    itemAttribs += "&nbsp;";

   

  // return the final result

  return itemAttribs;

}

 

This procedure checks four attributes: Archive, ReadOnly, Hidden, and System. If the attribute being checked is set, the procedure adds its first letter to the description string that will be returned. Otherwise, if the attribute is not set, a space is added. So, for example, the string "A  S" means Archive + System, while "ARH " means Archive + ReadOnly + Hidden.


If you look back at the code that calls this procedure, you'll see that the font of the label displaying the attribute description is set to Courier. This is because Courier is a fixed-width font, meaning that, for example, 'i' and 'Z' have the same width. Another reason for using Courier is that spaces have the same width as letters, and this allows us to create virtual columns and have all the A letters in the column aligned, and the same for the letters R, H, and S. If an attribute is not set for an item, you see a blank space as large as the letter above it, and then the other attribute letters (if any).

 

You may also wonder why we created a Label control instead of directly setting the cell's Text property. The reason is that we'll add another control to this column later in the chapter, and it's easier to add two controls to the Controls collection than to set a very long string for the cell's
Text property.

Displaying the Item Size

The next column we want to add displays the size of the item. It is easy to get this information for the files, since the FileInfo class exposes a Length property. As the DirectoryInfo class does not have a Length property, we need to calculate the size of a directory by summing the size of all its child files. Here is a procedure that uses recursion to sum the child files of all subdirectories and obtain the overall size of the directory:

 

protected long GetDirectorySize(string path)

{

  long dirSize = 0;

  DirectoryInfo dir = new DirectoryInfo(path);

 

  // add the size of each file

  foreach (FileInfo file in dir.GetFiles())

    dirSize += file.Length;

 

    // add the size of each subdirectory, retrieved by

    // recursively calling this same routine

    foreach (DirectoryInfo subdir in dir.GetDirectories())

      dirSize += GetDirectorySize(subdir.FullName);

 

  return dirSize;

}

 

Now we can easily get the size for both the files and the directories. But the size is returned in bytes, and a series of six or seven figures is not very readable. It would be better to show the size in bytes, kilobytes, or megabytes according to the number of bytes, as follows:

 

q        Less than 1024 bytes: show the size in bytes

q        Between 1024 and 1,048,576 bytes: show the size in KB (for example 2.52 KB, instead of 2,580 bytes)

q        1,048,576 bytes and above: show the size in MB


The following procedure accepts the size in double format, and returns a string with the size formatted in bytes, KB, or MB by following the rules above:

 

protected string FormatSize(double fileSize)

{

  if (fileSize < 1024)

    return String.Format("{0:N0} B", fileSize);

  else if (fileSize < 1024*1024)

    return String.Format("{0:N2} KB", fileSize/1024);

  else

    return String.Format("{0:N2} MB", fileSize/(1024*1024));

}

 

Now that we can get the size for a directory, and format any size in the proper way, let's add the Size column for each item. As we've done for the Attributes column, we're going to insert an empty cell for the first row, which links to the parent folder, and a new cell with the size for all files and folders. Here are the necessary updates:

 

if (folderPath != "/")

{

  // create the cell with the FolderUp icon,

  // and add the link that points to the parent directory

  ...

   

  // add the cells to the new row

  rowItem.Cells.Add(cellItemIcon);

  rowItem.Cells.Add(cellItemLink);

  rowItem.Cells.Add(new TableCell());

  rowItem.Cells.Add(new TableCell());

 

  // add the row to the table

  ...

}

 

foreach (DirectoryInfo childDir in childDirs)

{

 

  // create the required cells and controls

  cellSize = new TableCell();     

  ...

               

  // create the icon, the link that points to this subdirectory

  // and the cell that shows the attributes

  ...

   

  // set the cell that displays the item's size

  cellSize.Text = FormatSize(GetDirectorySize(childDir.FullName));

  cellSize.HorizontalAlign = HorizontalAlign.Right;

 

  // add the cells to the new row and add the row to the table

  rowItem.Cells.Add(cellSize);

  ...

}


// now add each child file

foreach (FileInfo childFile in childFiles)

{

 

  // create the required cells and controls

  cellSize = new TableCell();     

  ...

   

  // create the icon, the link that points to this file

  // and the cell that shows the attributes

  ...

   

  // set the cell that displays the item's size

  cellSize.Text = FormatSize(childFile.Length) + "&nbsp;";

  cellSize.HorizontalAlign = HorizontalAlign.Right;

 

  // add the cells to the new row and add the row to the table

  rowItem.Cells.Add(cellSize);

  ...

}

 

DevASP.Net - Disclaimer - Privacy
© 2002-2010 DevASP.net