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:
And of course Google also uses breadcrumbs:
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:
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:
Here are main features:
-
The No Links breadcrumb shows a static breadcrumb trail with no links.
The text is displayed using bold font, as is the separator.
-
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.
-
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:
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:
Function |
Description |
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();
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(" > "));
switch (index)
{
default:
case 0:
m_ccb[index].SetCrumbs(SAMPLE_1, _T('~'));
break;
case 1:
m_ccb[index].SetCrumbs(SAMPLE_2, 4);
break;
case 2:
m_ccb[index].SetCrumbs(SAMPLE_3, 5);
break;
}
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
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.