Click here to Skip to main content
15,867,686 members
Articles / Multimedia / GDI
Article

XBreadCrumbBar - Draw breadcrumb trail with hyperlinks and HTML

Rate me:
Please Sign up or sign in to vote.
5.00/5 (24 votes)
7 Aug 2007CPOL6 min read 62.5K   812   47   6
XBreadCrumbBar is a windowless non-MFC class that allows you to display a breadcrumb trail as HTML text, with support for web links and APP: links.

Introduction

According to Wikipedia,

Breadcrumbs or breadcrumb trails are a navigation technique used in user interfaces. Its purpose is to give users a way to keep track of their location within programs or documents. The term is taken from the trail of breadcrumbs left by Hansel and Gretel in the popular fairytale.

Here is typical way breadcrumbs are used on web:

screenshot

And of course Google also uses breadcrumbs:

screenshot

Use of breadcrumbs is becoming widespread on the web, and has started to show up in PC apps as well. Vista Windows Explorer uses breadcrumbs to show where you are in file system:

screenshot

Breadcrumb navigation seems particularly well-suited to any kind of hierarchical system, and serves two purposes: first, to show you where you are in hierarchy; and second, to allow you to navigate to a previous point in hierarchy in a random-access fashion - in other words, you do not have to keep hitting back button, you can just click on second link and immediately go to second location in breadcrumb trail.

While most studies of breadcrumb navigation have been focused entirely on web sites (see references), there are many similarities between navigating a web site and navigating a non-trivial PC app, especially one that deals with hierarchical data - for example, HR systems, auto parts ordering systems, and software for presenting and selecting class schedules. Jakob Nielsen, a user interface expert, has an interesting blog about breadcrumbs. If you replaced the words "web site" with "PC app" in his blog, it would still make perfect sense.

Breadcrumb navigation seemed appropriate for a large inventory system I was recently involved with, and the client liked the idea when I presented it to him. This led to the creation of XBreadCrumbBar, which is based on XHtmlDraw. This gives additional flexibility in the visual display of the breadcrumb trail, and also offers built-in support for both web links and APP: links (see my XHtmlDraw article for more details).

XBreadCrumbBar Features

Let me start by showing you the demo app:

screenshot

Here are main features:

  1. The No Links breadcrumb shows a static breadcrumb trail with no links. The text is displayed using bold font, as is the separator.
  2. The Web Links breadcrumb shows use of colors for links, copied from my favorite book site. The last crumb is different color and is not a link. Note that crumbs are bold, while separator is not.
  3. The APP: Links breadcrumb shows different colors for each link, with cursor on first link. When link is clicked, this is what you will see:

    screenshot

XBreadCrumbBar Implementation

The class CXBreadCrumbBar is derived from CXHtmlDraw, which you can find described in this article, and embeds the link-handling class CXHtmlDrawLink, also described in CXHtmlDraw article.

Here are functions of CXBreadCrumbBar class:

FunctionDescription
int Draw(HWND hWnd, BOOL bUnderlineUrl, int index = -1)Draw the XBreadCrumbBar to hWnd. If index = -1, draw all crumbs, otherwise only draw specified crumb.
int Draw(HDC hdc, BOOL bUnderlineUrl, int index = -1)Draw the XBreadCrumbBar to hdc. If index = -1, draw all crumbs, otherwise only draw specified crumb.
BOOL GetBoldSeparator()Get state of bold flag for separator
int GetCount()Retrieve number of crumbs
XHTMLDRAWSTRUCT * GetDrawStruct(int index)Retrieve draw struct for crumb specified by index
size_t GetCrumb(int index, BOOL bStripHtml, LPTSTR pszCrumb, size_t size)Retrieve crumb string for crumb specified by index. If pszCrumb is NULL, only the crumb length (in TCHARs) is returned.
int GetCrumb(LPCTSTR lpszCrumb, BOOL bStripHtml)Retrieve crumb index for crumb whose string matches lpszCrumb
CXHtmlDrawLink * GetLinks()Retrieve pointer to CXHtmlDrawLink object, used for web and APP: links
int GetCrumbs(TCHAR *** crumbs)Retrieve pointer to array of pointers to crumb strings
TCHAR * GetSeparator()Retrieves pointer to separator string
CXBreadCrumbBar& InitDrawStruct(XHTMLDRAWSTRUCT * pXHDS, BOOL bBoldSeparator = TRUE)Initialize draw struct
int IsAnchorUnderlined()Returns index number of crumb that is underlined, otherwise returns -1
int IsOverAnchor(HWND hWnd)Returns index number of crumb that mouse is over, otherwise returns -1
BOOL RelayClick(HWND hWnd)Relays click event to CXBreadCrumbBar
void RelayMouseMove(HWND hWnd)Relays mouse move to CXBreadCrumbBar
CXBreadCrumbBar& SetAppCommands(XHTMLDRAW_APP_COMMAND * paAppCommands, int nAppCommands)Sets up table for APP: commands
CXBreadCrumbBar& SetBoldSeparator(BOOL bBold)Sets separator bold flag: if TRUE, separator will be bold when crumb is bold; if FALSE, separator will not be bold, even when crumb is bold
CXBreadCrumbBar& SetCrumbs(const TCHAR * crumbs[], int count)Set crumb strings from array of strings
CXBreadCrumbBar& SetCrumbs(LPCTSTR crumbs, TCHAR sepchar)Set crumb strings from character-separated string, where character is specified by sepchar
CXBreadCrumbBar& SetSeparator(LPCTSTR lpszSep)Sets separator string

How To Use

Follow these steps to integrate XBreadCrumbBar into your app:

Step 1: Add XBreadCrumbBar Files

Add following files to your project:

  • XBreadCrumbBar.cpp
  • XBreadCrumbBar.h
  • XHtmlDraw.cpp
  • XHtmlDraw.h
  • XHtmlDrawLink.cpp
  • XHtmlDrawLink.h
  • XNamedColors.cpp
  • XNamedColors.h
  • XString.cpp
  • XString.h
NOTE: For all of the above .cpp files, you must select Not using precompiled headers in Visual Studio.

Step 2: Define Persistent CXBreadCrumbBar Object

In the demo app, there are three CXBreadCrumbBarM objects defined in XBreadCrumbBarTestDlg.h:

CXBreadCrumbBar m_ccb[3];

Step 3: Initialize XHTMLDRAWSTRUCT Struct

The XHTMLDRAWSTRUCT struct contains formatting information and screen position for XBreadCrumbBar. Here is the initialization function from demo app:

//=================================================================
void CXBreadCrumbBarTestDlg::InitDrawStruct(int index)
//=================================================================
{
    ASSERT((index >=0) && (index < 3));

    LOGFONT lf;
    memset(&lf, 0, sizeof(LOGFONT));
    CFont *pFont = GetFont();    // get font for the dialog
    if (pFont)
        pFont->GetLogFont(&lf);

    CWnd *pWnd = GetDlgItem(g_uStatic[index]);
    ASSERT(pWnd);

    CRect rect;
    pWnd->GetWindowRect(&rect);
    ScreenToClient(&rect);
    rect.DeflateRect(5, 5);
    rect.top += 10;

    CXBreadCrumbBar::XHTMLDRAWSTRUCT ds;

    ds.crText           = m_rgbText;
    ds.nID              = index;
    if (index == 0)
        ds.crText = RGB(119,121,118);
    if (index == 1)
        ds.crBackground = GetSysColor(COLOR_BTNFACE);
    else
        ds.crBackground = m_rgbBackground;
    ds.rect             = rect;
    if (index != 2)
        ds.bBold        = TRUE;
    else
        ds.bBold        = FALSE;
    ds.bLogFont         = TRUE;
    if (index == 0)
        _tcscpy(lf.lfFaceName, _T("Arial"));
    memcpy(&ds.lf, &lf, sizeof(LOGFONT));

    if (index == 0)
        m_ccb[index].SetSeparator(_T(" > "));

    // must set crumbs before calling CXBreadCrumbBar::InitDrawStruct

    switch (index)
    {
        default:
        case 0: 
            // all crumbs in one string, separated by '~'
            m_ccb[index].SetCrumbs(SAMPLE_1, _T('~'));    
            break;

        case 1: 
            // array of 4 strings
            m_ccb[index].SetCrumbs(SAMPLE_2, 4);    
            break;

        case 2: 
            // array of 5 strings
            m_ccb[index].SetCrumbs(SAMPLE_3, 5);    
            break;
    }

    // don't use bold separators in second bread crumb bar
    m_ccb[index].InitDrawStruct(&ds, (index == 1) ? FALSE : TRUE);
}

Step 4: Initialize APP: Commands

APP: hyperlinks allow you to display hyperlinked text that sends a Windows message to a specified window when user clicks on it. To use APP: hyperlinks, you must set up APP: command table - here is table used in the demo app:

CXHtmlDraw::XHTMLDRAW_APP_COMMAND AppCommands[] =
{
    { m_hWnd, WM_APP_COMMAND_1, 1, _T("WM_APP_COMMAND1") },
    { m_hWnd, WM_APP_COMMAND_2, 2, _T("WM_APP_COMMAND2") },
    { m_hWnd, WM_APP_COMMAND_3, 3, _T("WM_APP_COMMAND3") },
    { m_hWnd, WM_APP_COMMAND_4, 4, _T("WM_APP_COMMAND4") },
    { m_hWnd, WM_APP_COMMAND_5, 5, _T("WM_APP_COMMAND5") },
};
This table has five entries, but you can add as many entries as you need. Each entry has four elements: the first is the HWND of the window that is to receive the message; the second is the numeric message number that will be sent to the window via SendMessage(); the third is user-defined data that is returned in the wParam member; and the fourth is a string that ties the table entry to the HTML code.

After setting up this table, pass it to the CXBreadCrumbBarM object by calling SetAppCommands():

m_ccb[2].SetAppCommands(AppCommands,
    sizeof(AppCommands)/sizeof(AppCommands[0]));

You can call SetAppCommands() as often as you need to, if crumbs are changing dynamically.

Step 5: Track Mouse Movement

Since CXBreadCrumbBar is windowless, you must add code to relay mouse moves to CXBreadCrumbBar. Here is OnMouseMove() function from demo app:

//=============================================================================
void CXBreadCrumbBarTestDlg::OnMouseMove(UINT nFlags, CPoint point) 
//=============================================================================
{
    for (int i = 0; i < 3; i++)
        m_ccb[i].RelayMouseMove(m_hWnd);

    CDialog::OnMouseMove(nFlags, point);
}

Step 6: Handle Mouse Clicks

Again, since CXBreadCrumbBar is windowless, you must add code to relay clicks to CXBreadCrumbBar. Here is OnLButtonUp() function from demo app:

//=============================================================================
void CXBreadCrumbBarTestDlg::OnLButtonUp(UINT nFlags, CPoint point) 
//=============================================================================
{
    for (int i = 0; i < 3; i++)
    {
        if (m_ccb[i].RelayClick(m_hWnd))
            break;
    }

    CDialog::OnLButtonUp(nFlags, point);
}

References

Links to information on breadcrumb navigation:

Links to articles I have used in demo app:

Revision History

Version 1.0 - 2007 August 7

  • Initial public release

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
BugHeap Corruption Pin
Member 859911427-Mar-12 9:09
Member 859911427-Mar-12 9:09 
First off this thing is great, thanks for making and sharing it, looks phenomenal in any application.

I noticed I was corrupting my heap when I tried using it dynamically to show the progression of searches in a program I was writing. I set the number of crumbs to 0 at certain points when users restart their search in order to erase the crumbs that no longer represent the results shown. This initiates the cleanup code deleting the m_crumbs array, but the SetCrumbs code for TCHAR arrays does not create a new instance of the array if 0 is reported as the number of crumbs! The next time the cleanup code is run the original pointer for the crumb array is still held by m_crumbs even though it has already been deleted before but not set to NULL. The cleanup code dutifully deletes that same address again which now contains who knows what in memory and the situation devolves from there. I think setting m_crumbs to NULL after deleting it should do the trick.
QuestionC# Pin
The Cake of Deceit27-Jun-08 5:09
The Cake of Deceit27-Jun-08 5:09 
AnswerRe: C# Pin
Hans Dietrich27-Jun-08 11:53
mentorHans Dietrich27-Jun-08 11:53 
GeneralRe: C# Pin
The Cake of Deceit28-Jun-08 10:44
The Cake of Deceit28-Jun-08 10:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.