TreeView Control With Custom Icons in ASP.Net Using SiteMap

Introduction 
 
The TreeView control is capable of many possible appearances, providing flexible image customization as well as properties that specify custom user interface (UI) options.
We can use images with the TreeView control to represent nodes, connecting lines, and the expand and collapse icons and can also use a predefined set of images from the ImageSet property, or use custom images by setting the individual image properties for each of its nodes.
 
Background
 
My Website is also hierarchical in structure so I knew that a TreeView control was required. The problem was, how to show multiple icons in the treeview, where each icon represents a specific type. The icons must also show a tooltip when hovered over to display the full name of the type using the Sitemap with Sitemap Data Source control .
 
SiteMap Menu with Custom icons
 
While binding a  TreeView to a SiteMapDataSource is very easy, it can prove challenging to determine how to extend the site map with custom properties and use these extended properties in a TreeView . This sample shows how to add custom icons to a sitemap-driven menu.

SiteMap allows the addition of custom attributes to your site map nodes. If you're using the XML site map provider, it's as simple as this:

<siteMapNode url="default.aspx" title="Home" description="The home page" imageUrl="home.gif">

Here, imageUrl is a custom property, not defined by the SiteMap infrastructure by default.

The first idea that comes to mind is to use these custom attributes to just replace the automatic bindings you get from binding a TreeView to a SiteMapDataSource with "manual" bindings. This works very well with SiteMapDataSource because it is implemented so that the nodes implement an ICustomTypeDescriptor, so any XML attribute is exposed as a property on the node as far as reflection is concerned. This is a very powerful feature of .NET reflection that gives it some of the qualities of dynamic languages. Unfortunately, SiteMapDataSource does not implement this, nor does Treeview know how to query custom site map attributes. This is an oversight and support for that might be added in future releases.

An easy (although not declarative) way out of this problem is to hook the OnTreeNodeDataBoundevent and to set the custom properties from there as in the following:

protected void TreeView1_TreeNodeDataBound(object sender, TreeNodeEventArgs e)

{
    // Reference the underlying SiteMapNode object...

    SiteMapNode nodeFromSiteMap = (SiteMapNode)e.Node.DataItem;

   // If we have an imageUrl value, assign it to the TreeView node's ImageUrl property

   if (nodeFromSiteMap["imageUrl"] != null)

   e.Node.ImageUrl = System.IO.Path.Combine("~/Images/", nodeFromSiteMap["imageUrl"]);

}
 
The Tree View Control looks like this:

<asp:TreeView ID="TreeView1" DataSourceID="SiteMapDataSource1"

NodeStyle-CssClass="treeNode" RootNodeStyle-CssClass="rootNode" LeafNodeStyle-CssClass="leafNode"
EnableViewState
="False" runat="server" ImageSet="BulletedList" ShowLines="false" OnTreeNodeDataBound="TreeView1_TreeNodeDataBound">
  <
ParentNodeStyle Font-Bold="False" />
  <
HoverNodeStyle Font-Underline="True" ForeColor="#5555DD" />
  <
SelectedNodeStyle Font-Underline="True" ForeColor="#5555DD"
  HorizontalPadding="0px" VerticalPadding="0px" />
  <
NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black"
  HorizontalPadding="5px" NodeSpacing="0px" VerticalPadding="0px" />
</
asp:TreeView>
<
asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />

The SiteMap structure looks like the following:

 

<?xml version="1.0" encoding="utf-8" ?>

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

  <siteMapNode title="Home" description="Home" roles="*" url="" >

    <siteMapNode url="Admin/Home.aspx" title="Admin Home" imageUrl="Home-icon.png" roles="Admin" />

    <siteMapNode url="Agent/Home.aspx" title="Agent Home" roles="Agent" />

    <siteMapNode title="Services" description="Services" roles="Admin,Agent" imageUrl="customer-service-icon.png">

      <siteMapNode url="Admin/AddUser.aspx" title="Add User" roles="Admin" />

      <siteMapNode url="Admin/DeleteUser.aspx" title="Delete user" roles="Admin" />

      <siteMapNode url="Agent/SearchUser.aspx" title="Search User" roles="Agent" />

      <siteMapNode url="Agent/ViewUser.aspx" title="View User" roles="Agent" />

    </siteMapNode>

    <siteMapNode title="UserMangement" description="Services" roles="Admin,Agent">

      <siteMapNode url="Admin/AddUser1.aspx" title="Add User" roles="Admin" />

      <siteMapNode url="Admin/DeleteUser1.aspx" title="Delete user" roles="Admin" />

      <siteMapNode url="Agent/SearchUser1.aspx" title="Search User" roles="Agent" />

      <siteMapNode url="Agent/ViewUser1.aspx" title="View User" roles="Agent" />

    </siteMapNode>

    <siteMapNode title="reports" description="Services" roles="Admin,Agent">

      <siteMapNode url="Admin/AddUser2.aspx" title="Add User" roles="Admin" />

      <siteMapNode url="Admin/DeleteUser2.aspx" title="Delete user" roles="Admin" />

      <siteMapNode url="Agent/SearchUser2.aspx" title="Search User" roles="Agent" />

      <siteMapNode url="Agent/ViewUser2.aspx" title="View User" roles="Agent" />

    </siteMapNode>

  </siteMapNode>

</siteMap>


TreeViewControl
The final output looks as in the previous image.