Introduction
The term Breadcrumb comes from the story of Hansel and Gretel. While going through the forest, they left breadcrumbs on the way to remember the route. Many of today’s Web sites today (for example, Yahoo) use this concept. A user surfing the site is inside a forest. Breadcrumbs in Web sites show the path the user has taken to reach the current page. They can also tell the hierarchy of the page inside the site. Some sites depend on the directory structure of the site to form the breadcrumb. It splits the virtual path of the directory and shows each directory as a link in the breadcrumb. Here, I will design a breadcrumb that forms the links dynamically by remembering the path the user has taken. It can also remember the querystring passed to the page on the last visit.
Assumptions
I make some assumptions about the breadcrumb. These assumptions act as the requirements for my breadcrumb. If anybody has a different set of requirements, we can think of enhancing the system.
- Each page has a level in the site. This level determines the hierarchy of the page irrespective of the directory structure. The home page is Level 1.
- The user always goes from higher-level pages to lower-level pages. For example, the navigation will be 1-2-3 or 1-2-4, but not 1-4-2.
- If the user comes back to the higher level from a lower level, we can ignore the lower levels. We are not forming a cycle in the breadcrumb. In other words, there won’t be a breadcrumb in the form 1-2-3-4-2. If such navigation happens, the Breadcrumb will be 1-2.
- The breadcrumb will always start with Level 1. If the user directly comes to Level 4 (by using a short cut), the breadcrumb should show 1-4.
- The breadcrumb should remember the querystring variables of the page.
- Each page declares its name to appear in the breadcrumb.
Design and Implementation
Selection of objects
In the breadcrumb, we need to track the state of the previous pages. In ASP.NET, we have a Session object to keep track of the state. The session object should store the state information of multiple pages. So, it would be better to choose any of the collection classes. According to Assumption 2, the breadcrumb should keep an order based on the level. This made me choose SortedList as my collection. We need to store the name, querystring, and url of the page to create the breadcrumb. A structure by the name of PageCrumb can do it.
Implementation strategy
Our BreadCrumb control accepts the Level and Name of the Page from the container page and creates a PageCrumb object. It can get the url and querystring from the request object. The control modifies the SortedList by comparing the levels of the existing PageCrumb objects in the list. The modification means the removal of all lower-level PageCrumb objects from the list. Once the modification is complete, the control can create the breadcrumb by iterating through the SortedList. The BreadCrumb control contains a label whose Text property can be set as our dynamically created breadcrumb.
The Code
The following structure represents the class that can hold the level, url, and linkName of the page. I also keep the level inside the PageCrumb to make it more meaningful even though it is not required for the implementation.
Public Structure PageCrumb Private _level As Short Private _url As String Private _linkName As String //We are setting all the properties at the time of construction Public Sub New(ByVal level As Short, ByVal url As String, ByVal linkName As String) _level = level _url = url _linkName = linkName End Sub //We are making all the properties as read-only. //We are not expecting it to change once it is set. Public ReadOnly Property Level() As Short Get Return _level End Get End Property Public ReadOnly Property Url() As String Get Return _url End Get End Property Public ReadOnly Property LinkName() As String Get Return _linkName End Get End Property End Structure
The Vb file of the BreadCrumbControl will contain the following code
Declare the following variables:
'Variable holding the Link name of the page Private _tailName As String 'Variable holding the level of the page Private _level As Short 'The pagecrumb object of the current page Private _pageCrumb As New PageCrumb 'We will use a sorted list as we can use the level as key Private _crumbList As SortedList
Define the following properties:
'Each page has a level. The page should declare its level Public Property Level() As Short ' TO DO: We can check for some constraints here Get Return _level End Get Set(ByVal Value As Short) _level = Value End Set End Property 'Each page needs a meaningful name of it. Let them declare it Public Property TailName() As String ' TO DO: We can check for some constraints here Get Return _tailName End Get Set(ByVal Value As String) _tailName = Value End Set End Property
Modify the Page_Load event by adding the following code. We are creating the PageCrumb object by using the Request.RawUrl. This RawUrl property also contains querystring information. This information helps us to open the page with the querystring once the user comes back to the page.
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load 'We are not disabling viewstate If Not (Page.IsPostBack) Then 'Minimum level is 1 If (_level <= 0) Then _level = 1 End If 'If no friendly name gives Untitled as default If (_tailName = "") Then _tailName = "Untitled" End If 'Create a Crumb object based on the properties of this page _pageCrumb = New PageCrumb(_level, Request.RawUrl, _tailName)'Check that our Crumb is there in the session...if not, create 'and add it...else get it If Session.Item("HASH_OF_CRUMPS") Is Nothing Then crumbList = New SortedList Session.Add("HASH_OF_CRUMPS", _crumbList) Else _crumbList = Session.Item("HASH_OF_CRUMPS") End If 'Now modify the List of the breadcrumb ModifyList() 'Put the breadcrumb from the session of sortlist PutBreadCrumbs() End If End Sub
The following function checks the level of the page. If the level is 1, it removes everything from the list and adds the home link. I assume that the Home page is always the first-level page. If there is nothing in the list, we add the Home page to the list. Please remember our Assumption 4 here. Also note the GlobalMembers.HOME_LINK_PATH. It is a string that represents the Url of the home page.
Private Sub ModifyList() 'Remove all Entries from the list that are higher or equal in level 'because at a level there can be max of 1 entry in the list RemoveLowerLevelCrumbs() 'If level is 1 set the Crumb as home If _pageCrumb.Level = 1 Then _crumbList.Clear() _crumbList.Add(CType(1, Short), New PageCrumb(1, _ GlobalMembers.HOME_LINK_PATH, "Home")) Else 'If nothing in the list adds the home link first If _crumbList.Count = 0 Then _crumbList.Add(CType(1, Short), New PageCrumb(1, _ GlobalMembers.HOME_LINK_PATH, "Home")) End If 'Now add the present list; also, no other check is required here 'as we have cleaned up the List at the start of the function _crumbList.Add(_level, _pageCrumb) End If End Sub
As per our Assumption 3, the breadcrumb should not create a cycle. The following function removes all lower-level pages from the list.
'Function will remove all the entries from the list that are higher 'than or equal to the present level Private Sub RemoveLowerLevelCrumbs() Dim level As Short Dim removalList As New ArrayList(_crumbList.Count) For Each level In _crumbList.Keys If (level >= _level) Then removalList.Add(level) End If Next 'Now remove all keys in the list For Each level In removalList _crumbList.Remove() Next End Sub
Now, we can create the breadcrumb by using the list. I am using > as the separator. Also, the last name in the breadcrumb is not a link because it denotes the current page.
Private Sub PutBreadCrumbs()
Dim linkString As New StringBuilder
Dim pageCrumb As New PageCrumb
Dim index As Integer
For index = 0 To _crumbList.Count - 2
pageCrumb = _crumbList.GetByIndex)
linkString.Append(String.Format("<a href = {0} >{1} </a>", _
pageCrumb.Url, pageCrumb.LinkName))
linkString.Append(" > ")
Next index
'Add the tail also
pageCrumb = _crumbList.GetByIndex(index)
linkString.Append(pageCrumb.LinkName)
lblTrail.Text = linkString.ToString()
End Sub
End Class
How to Use This Control
Add this user control to any page on which you need a breadcrumb. Then, set the Level and Name properties of the control. I have attached some sample files showing the usage of the control. They also contain the complete source code of the implementation.
Limitations
The above-described BreadCrumb cannot remember the actions happening on the page during postback. We can expose an additional property of the Object type in the PageCrumb object to do this. The calling page can use this object to store any additional information it needs. But, this requires some coding in the container page, so I avoided it. Somebody else can take a better shot on this. Because we are storing the information in the session, we should be a little careful about the number of levels our Web site will have.