Creating a custom Bread Crumb control in asp.net

Hi,
    We all might be aware of the bread crumbs shown on so many websites. Bread crumbs help users in a big way to navigate the website in a structured manner. Asp.net provides a very good sitemap control which can be used to display a bread crumb. This uses a sitemap file which is actually a xml file indicating the struture and hierarchy of your web site.

   A few days ago a colleage of mine ran into soem issues with the standard asp.net sitemap control. It became evident that managing and keeping track of query string was not easy in the standard sitemap control. 

  This motivated me to develop a custom bread crumb control. The control i developed was a pretty simple one. I will  discuss about this control and explain in detail about the code.
    Some of the features of this bread crumb control are -
    1. It keeps track of the query string of previous pages browsed.
    2. It doesnt require any xml file.
   
    This is a very simple control and as you can see it can be developed further. The asp.net sitemap control is superior to it in a lot of ways and i will recommend users to use it unless you need new features.

     The breadcrumb consists of two main classes. - BreadCrumbNode and BreadCrumb

     BreadCrumbNode represents a visited URL or the current page, in addition it contains information like the caption/title to be shown on the breadcrumb. You can modify the class to include more information.

     The other class is the BreadCrumb which represents the BreadCrumb Control. Below is the code for both the classes - 

namespace Web.Custom.Controls
{

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    /// <summary>
    /// Summary description for BreadCrumbNode
    /// </summary>
    ///
    public class BreadCrumbNode
    {
        private String strTitle = string.Empty;
        private string strURL = string.Empty;
        public BreadCrumbNode(String strTitle)
        {
            this.strTitle = strTitle;
            strURL = HttpContext.Current.Request.Url.AbsoluteUri; //picks up the current url along with the query string
        }

        public string Title
        {
            get
            {
                return strTitle;
            }
        }

        public string URL
        {
            get
            {
                return strURL;
            }
        }


    }
}



namespace Web.Custom.Controls
{
    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Collections.Generic;
    using System.ComponentModel;
    /// <summary>
    /// Summary description for BreadCrumb
    /// </summary>

    public class BreadCrumb : WebControl
    {

        public BreadCrumb()
        {

        }


       /*The below property stores the lastvisted urls (represented by the class BreadCrumbNode) in  a stack. 
         This stack is then stored in the session. 
          The strURLGroup is required in case ur site has multiple different sections or to be more clear multiple hierarchies with each having a different root/home page
       */
        private Stack<BreadCrumbNode> URLList
        {
            get
            {
                if (HttpContext.Current.Session["KEY_" + strURLGroup] == null)
                {
                    HttpContext.Current.Session["KEY_" + strURLGroup] = new Stack<BreadCrumbNode>();
                }

                return (Stack<BreadCrumbNode>)HttpContext.Current.Session["KEY_" + strURLGroup];

            }
            set
            {
                HttpContext.Current.Session["KEY_" + strURLGroup] = value;
            }
        }


        /* This function may be required to be called from each individual page.      */   
        public void Add(BreadCrumbNode bcn)
        {

           /* search if this page already exists. If it exists then remove it and also all other pages after it. This is like i am going to the parent page from a child page  */
            BreadCrumbNode[] bcnArr = URLList.ToArray();
            bool nodeExists = false;
            for (int cnt = 0; cnt < bcnArr.Length; cnt++)
            {
                if (bcn.Title.Equals(bcnArr[cnt].Title))
                    nodeExists = true;
            }

              //if the page exists then remove the nodes after it
             if (nodeExists)
            {
                BreadCrumbNode bcnTemp = null;
                do
                {
                    bcnTemp = URLList.Pop();
                } while (!bcnTemp.Title.Equals(bcn.Title));
            }

            URLList.Push(bcn); //push in the current node


        }

        public void Add(string strTitle)
        {
            Add(new BreadCrumbNode(strTitle));

        }


//The levels function represent the level of the hierarchy at which  a page is
// It may be required that a page calls the level function corresponding to the level it is in the hierarchy
//Like a home page will call level0, a level1 page will call level1 and child may call level2
//This is not  required but to maintan stack in a consistent state even when the user skips the strutured navigation
//of a wesite that may happen by directly typing in the url in place of using the menu links 

        public void Level0()
        {
           
                URLList.Clear();//clear all the nodes as this is the root/home page
           
        }

        public void Level1()
        {
            BreadCrumbNode[] bcnArr = URLList.ToArray();
            if (bcnArr.Length > 0)
            {
                URLList.Clear();
                URLList.Push(bcnArr[bcnArr.Length - 1]);
            }// leave the home page alone

        }

        public void Level2()
        {
            BreadCrumbNode[] bcnArr = URLList.ToArray();
            if (bcnArr.Length > 1)
            {
                URLList.Clear();
                URLList.Push(bcnArr[bcnArr.Length - 1]);
                URLList.Push(bcnArr[bcnArr.Length - 2]);
            }

        }

         //finally render the links according to the data in teh stack
        protected override void Render(HtmlTextWriter writer)
        {
           // base.Render(writer);
            BreadCrumbNode[] bcnArr = URLList.ToArray();
            if (bcnArr.Length > 0)
            {
                for (int cnt = bcnArr.Length -1 ; cnt > 0; cnt--)
                {
                    writer.Write("<A href=\"");
                    writer.Write(bcnArr[cnt].URL);
                    writer.Write("\">");
                    writer.Write(bcnArr[cnt].Title);
                    writer.Write("</A>");
                    writer.Write(" | ");
                }

                writer.Write("<B>");
                writer.Write(bcnArr[0].Title);
                writer.Write("</B>");
            }
        }

 


        private String strURLGroup = "";


    }
}

The usage of the above classes is shown

aspx side
<%@ Register Namespace="Web.Custom.Controls" TagPrefix="bc" %>

in the body  section
<bc:BreadCrumb runat="server" ID="bc1" />

codebehind file

   protected void Page_Load(object sender, EventArgs e)
    {
        bc1.Level0();
        bc1.Add("Home");
    }

for the child of the above page  aspx remains the same
   protected void Page_Load(object sender, EventArgs e)
    {
        bc1.Level1();
        bc1.Add("section1");//this can be the caption you want like section2
    }

the output will be
Home | Section1
or
Home | Section2
as per the value of teh parameter in the   bc1.Add function

for page that is after the section1 page  
protected void Page_Load(object sender, EventArgs e)
    {
        bc1.Level2();
        bc1.Add("Child");//this can be the caption you want like section2
    }
The output will be
Home | Section1 | Child

I hope i have been clear enough for you to understand the code and the concept. I have attached the zip file that contains the code and a simple demo - BreadCrumb.zip. 

I help this post of mine will help to put in motion more ideas related to this. In case it does, do let me know.

Cheers
Sohail Sayed

No Comments