Home
Fractals
Tutorials
Books
My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331

Small C++ class to transform any static control into a hyperlink control for Windows

Olivier Langlois, IT superstar coach, Dominate LinkedIn Formula author
by Olivier Langlois

Contents





The demo program

The demo program is just a simple MFC AppWizard generated application where the About dialog class has been changed to demonstrate how to use my CHyperLink class.

First, in the Dialog editor, add some static controls. Make sure to select the TABSTOP style and to give the control a unique ID. Then I derive a new class from CHyperLink to override OnSelect() and OnDeselect(). I am sending the URL text to the status bar by calling the Frame window SetMessageText() function.

class CDemoLink : public CHyperLink
{
protected:
    virtual void OnSelect(void)
    { ((CFrameWnd *)AfxGetMainWnd())->
               SetMessageText(m_strURL); }
    virtual void OnDeselect(void)
    { ((CFrameWnd *)AfxGetMainWnd())->
               SetMessageText(AFX_IDS_IDLEMESSAGE); }
};

Then add member variables of type CDemoLink to the dialog class and do the following in the WM_INITDIALOG handler:

void CAboutDlg::setURL(CHyperLink &ctr, int id)
{
   TCHAR buffer[URLMAXLENGTH];
   int nLen = ::LoadString(AfxGetResourceHandle(),
                           id, buffer, URLMAXLENGTH);
   if( !nLen )
   {
     lstrcpy( buffer, __TEXT(""));
   }
    ctr.ConvertStaticToHyperlink(GetSafeHwnd(),id,buffer);
}

BOOL CAboutDlg::OnInitDialog()
{
   CAboutDlg::OnInitDialog();

   // TODO: Add extra initialization here
   setURL(m_DemoLink,IDC_HOMEPAGE);
   setURL(m_DemoMail,IDC_EMAIL);

   return TRUE;  // return TRUE unless you
                 // set the focus to a control
                 // EXCEPTION: OCX Property
                 // Pages should return FALSE
}

The demo program also demonstrates how to use CHyperLink with the tooltip control. To do so, I had to derive a new class from MFC CToolTipCtrl class. I am going to first show you the code using the new derived class and then explain the purpose of the new class:

BOOL CAboutDlgWithToolTipURL::OnInitDialog()
{
   CAboutDlg::OnInitDialog();

   // TODO: Add extra initialization here
   m_ctlTT.Create(this);
   setURL(m_DemoLink,IDC_HOMEPAGE);
   setURL(m_DemoMail,IDC_EMAIL);

   /*
    * It is OK to add a Window tool
    * to the tool tip control with
    * the CHyperLink dynamically
    * allocated URL string because
    * the windows are destroyed with
    * WM_DESTROY before the CHyperLink
    * destructor call where the URL string is freed.
    */
   m_ctlTT.AddWindowTool(GetDlgItem(IDC_HOMEPAGE)->GetSafeHwnd(),
                       (LPTSTR)m_DemoLink.getURL());
   m_ctlTT.AddWindowTool(GetDlgItem(IDC_EMAIL)->GetSafeHwnd(),
                       (LPTSTR)m_DemoMail.getURL());

   return TRUE;  // return TRUE unless you set the focus to a control
                 // EXCEPTION: OCX Property Pages should return FALSE
}

Note that m_ctlTT type is CSubclassToolTipCtrl and that AddWindowTool() is a new function. The reason why MFC CToolTipCtrl was not used directly is that the ToolTip control needs to receive the tool's messages in order to work. The low level ToolTip API provides different ways to achieve this but the MFC class only exposes one way of doing it. It is to relay the tool messages through CToolTipCtrl::RelayEvent(). It is relatively easy to use but it was inconvenient for CHyperLink as one of the design goals is to not force users to use MFC. The low level API could have been used with the message TTM_RELAYEVENT but still it would incur unnecessary overhead to all the hyperlinks not using tooltips. I found the solution in the book Programming Windows With MFC. It uses the ToolTip control feature that allows it to subclass the tools windows.

/*
 * class CSubclassToolTipCtrl
 */
class CSubclassToolTipCtrl : public CToolTipCtrl
{
// Operations
public:
/******************************************************************************
 *
 * Name      : AddWindowTool
 *
 * Purpose   : Add a window tool by using the Tooltip subclass feature
 *
 * Parameters:
 *     hWin    (HWND)    Tool window
 *     pszText (LPTSTR)  Tip text (can also be a string resource ID).
 *
 * Return value : Returns TRUE if successful, or FALSE otherwise.
 *
 ****************************************************************************/
   BOOL AddWindowTool( HWND hWin, LPTSTR pszText );

/******************************************************************************
 *
 * Name      : AddRectTool
 *
 * Purpose   : Add a rect tool by using the Tooltip subclass feature
 *
 * Parameters:
 *     hWin    (HWND)    Tool window parent
 *     pszText (LPTSTR)  Tip text (can also be a string resource ID).
 *     lpRect  (LPCRECT) Tool rect
 *     nIDTool (UINT)    User defined Tool ID
 *
 * Return value : Returns TRUE if successful, or FALSE otherwise.
 *
 ****************************************************************************/
   BOOL AddRectTool( HWND hWin, LPTSTR pszText, LPCRECT lpRect, UINT nIDTool );

// Implementation
   void FillInToolInfo(TOOLINFO& ti, HWND hWnd, UINT nIDTool) const;
};
/*
 * Function CSubclassToolTipCtrl::AddWindowTool
 */
BOOL CSubclassToolTipCtrl::AddWindowTool( HWND hWin, LPTSTR pszText )
{
    TOOLINFO ti;
    FillInToolInfo(ti,hWin,0);
    ti.uFlags  |= TTF_SUBCLASS;
    ti.hinst    = AfxGetInstanceHandle();
    ti.lpszText = pszText;

    return (BOOL)SendMessage(TTM_ADDTOOL,0,(LPARAM)&ti);
}

/*
 * Function CSubclassToolTipCtrl::AddRectTool
 */
BOOL CSubclassToolTipCtrl::AddRectTool( HWND hWin, LPTSTR pszText,
                                        LPCRECT lpRect, UINT nIDTool )
{
    TOOLINFO ti;
    FillInToolInfo(ti,hWin,nIDTool);
    ti.uFlags  |= TTF_SUBCLASS;
    ti.hinst    = AfxGetInstanceHandle();
    ti.lpszText = pszText;
    ::CopyRect(&ti.rect,lpRect);

    return (BOOL)SendMessage(TTM_ADDTOOL,0,(LPARAM)&ti);
}

// Implementation

/*
 * Function CSubclassToolTipCtrl::FillInToolInfo
 */
void CSubclassToolTipCtrl::FillInToolInfo(TOOLINFO& ti, HWND hWnd, UINT nIDTool) const
{
    ::ZeroMemory(&ti, sizeof(TOOLINFO));
    ti.cbSize   = sizeof(TOOLINFO);
    if (nIDTool == 0)
    {
        ti.hwnd = ::GetParent(hWnd);
        ti.uFlags = TTF_IDISHWND;
        ti.uId = (UINT)hWnd;
    }
    else
    {
        ti.hwnd = hWnd;
        ti.uFlags = 0;
        ti.uId = nIDTool;
    }
}

I added a minor improvement to the solution found in the Programming Windows With MFC book. I initialized the TOOLINFO structure to zero prior to using it. Initializing structures prior to using them is a good programming practice. If the structure definition is updated with the addition of new fields, without initializing the structure, you would have some fields unitialized with random values and this can lead to nasty hard to find bugs. By resetting the structure, you shield your code against that.

Conclusion

That is it! I hope you enjoyed this C++ Windows programming tutorial on my hyperlink control C++ class and I hope that the source code will be helpful to you in your projects. In the next section, you will find the books that I have consulted to build this C++ Windows programming tutorial. Those books are great and filled with Windows programming gems that you should know. It is strongly recommended that you get yourself a copy of these books especially since from time to time, you can find these books at a bargain price. Take the time to check the prices. This is maybe your lucky day today!

Bibliography

Revision history

  • 12-03-2005
    • Added alternatives that are WTL and comctlr32.dll version 6.
    • Added changes to make CHyperLink compatible with UNICODE.
    • Added an example on how to use CHyperLink with ToolTips.
  • 11-17-2005:
    • Original article.


Page 1 2

Back to the tutorials list

Home :: Fractals :: Tutorials :: Books :: My blog :: My LinkedIn Profile :: Contact