Hyperlink Control Manual

Author: Alexander Stoica (Berlin, Germany) nitex@freenet.de
Version: 0.2

If you find bugs, have suggestions, find translation errors or just want to write some constructive criticism you may mail me to the address above. You can write in English or German. You're free to use this control in your projects, even commercial. A short credit in the about box or documentation would be nice but is not required.

Special thanks to John Findlay, who helped me with the first version of  the hyperlink control.
http://www.john.findlay1.btinternet.co.uk

Also thanks to Pelle Orinius for making such a nice developement environment.
http://smorgasbordet.com/pellesc

I've decided not to use Pelles C integrated custom control feature, because i want to avoid shipping additional *.dll files with a program.


Content:

1. The structure members of HYPCTRL
2. Tutorial: How to use the hyperlink control
3. Tutorial: How to use the callback feature
4. Remarks


1. The structure members of HYPCTRL

First have a look at the HYPCTRL structure.

typedef struct {
    LPTSTR      szLink;
    LPTSTR      szText;
    LPTSTR      szTooltip;
    COLORREF    fgcNormal;
    COLORREF    fgcHover;
    COLORREF    bgcNormal;
    COLORREF    bgcHover;
    ULSTYLE     ulStyle;
    UINT        dtStyle;
    BOOL        bAutoSize;
    BOOL        bPlaySound;
    callback    cbFn;
    DWORD       cbID;
} HYPCTRL;

You'll not need to fill out all structure members for using the hyperlink control. You'll only modify the members as needed. Let's have a look at what each member is used for.

LPTSTR szLink;
This is the link which will be opened when the hyperlink control was clicked. It should include the protocol which will be used by ShellExecute() to open the required application. This structure member is required to fill out as long as no callbacks are used. If you use callbacks, this value is ignored. The default value is NULL.

Example:
hc.szLink = _T("http://smorgasbordet.com/pellesc");
hc.szLink = _T("mailto:someone@somewhere.net");

LPTSTR szText;
This is the text which will be displayed as link. This structure member is optional as long as no callbacks are used. If you use callbacks, this value is required. The default value is NULL. If you don't fill out this structure member, the value of szLink will be used as szText.

Example:
hc.szText = _T("Pelles C Website");

LPTSTR szTooltip;
This is the text which will be displayed as tooltip to provide additional information for the user. This value is optional. The default value is NULL.

Example:
hc.szTooltip = _T("Click here to visit Pelles C website");

COLORREF fgcNormal;
This is the text color. This value is optional. The default value is RGB(0, 0, 255); (blue).

Example:
hc.fgcNormal = RGB(0, 0, 255);

COLORREF fgcHover;
This is the text color when the mouse is over the control. This value is optional. The default value is RGB(255, 0, 0); (red).

Example:
hc.fgcHover = RGB(255, 0, 0);

COLORREF bgcNormal;
This is the background color. This value is optional. The default value is (COLORREF) GetSysColor(COLOR_3DFACE).

Example:
hc.bgcNormal = (COLORREF) GetSysColor(COLOR_3DFACE);

COLORREF bgcHover;
This is the background color when the mouse is over the control. This value is optional. The default value is (COLORREF) GetSysColor(COLOR_3DFACE).

Example:
hc.bgcHover = (COLORREF) GetSysColor(COLOR_3DFACE);

ULSTYLE ulStyle;
This value is used to define how the hyperlink control text will be underlined. This value is optional. The default value is ulAlways. ULSTYLE is defined like the following:

typedef enum {
    ulHover, ulNone, ulAlways
} ULSTYLE;

ulHover
ulNone
ulAlways
- underline text when mouse is over control
- never underline text
- always underline text

Example:
hc.ulStyle = ulAlways;

UINT dtStyle;
This value defines how the hyperlink control text will be drawn. This value is optional. The default value is DT_LEFT | DT_TOP. You can use the same flags which DrawText() uses.

Example:
hc.dtStyle = DT_LEFT | DT_TOP;

BOOL bAutoSize;
This value defines if the hyperlink control should resize to fit the text. This value is optional. The default value is TRUE.

Example:
hc.bAutoSize = TRUE;

BOOL bPlaySound;
This value defines if the hyperlink control should play a sound when clicked. This value is optional. The default value is TRUE. The sound will be located through the registry and is defined in the control panel applet "Sounds and Audio devices" -> "Sounds" -> "Windows Explorer" -> "Start navigating".

Example:
hc.bPlaySound = TRUE;

callback cbFn;
This is a pointer to a callback function which will be called when the hyperlink control was clicked. This value is optional. the default value is NULL. The callback function is defined like the following:

typedef void (*callback) (DWORD HypCtrlID);

Example:
hc.cbFn = HypCtrlCallback;

DWORD cbID;
This value can be used to identify a hyperlink control. This value is optional. The default value is 0. This value will be passed to the callback function as argument.

Example:
hc.cbID = 1;

#define HC_WEBSITE 1
hc.cbID = HC_WEBSITE;


2. Tutorial: How to use the hyperlink control

Copy the files "hyperlink.c" and "hyperlink.h" to your project directory and add them to your project. After that include the hyperlink header file in your source code.

#include "hyperlink.h"

The best place for creating hyperlink controls is while a window or dialog initializes. First we need to declare a variable of type HYPCTRL to store our modified values. You can use the same variable to create multiple hyperlink controls. In that example we will create two of them.

HYPCTRL hc;

The first thing we should do with our new variable is initializing. This is accomplished with a call to InitHypCtrl(). This function creates the hyperlink control class if it doesn't exists and initializes the HYPCTRL structure with default values.

InitHypCtrl(&hc);

Next we need to modify the values to suit our needs. We want the link to underline only if the mouse is over the control. We're also adding a tooltip text to the control.

hc.ulStyle = ulHover;
hc.szLink = _T("http://smorgasbordet.com/pellesc");
hc.szTooltip = _T("Visit Pelles C Website");


Now it's time to create the control. This is done with a call to CreateHypCtrl(). This function requires six parameters, the first is the handle to the parent window, the second is the address of our structure with the modified values, the third and fourth are the left and top positions of the control and the fifth and sixth value specifies the width and height of the control. You can pass 0 to the fifth and sixth parameter if the auto sizing feature is enabled (which is the default).

CreateHypCtrl(hwndDlg, &hc, 10, 10, 0, 0);

So the complete code for a website and mail link would look like this:

...
#include "hyperlink.h"
...

static LRESULT CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG: {
            HYPCTRL hc;

            InitHypCtrl(&hc);
            hc.ulStyle = ulHover;
            hc.szLink = _T("http://smorgasbordet.com/pellesc");
            hc.szTooltip = _T("Visit Pelles C Website");
            CreateHypCtrl(hwndDlg, &hc, 10, 10, 0, 0);

            InitHypCtrl(&hc);
            hc.ulStyle = ulHover;
            hc.szLink = _T("mailto:someone@somewhere.net");
            hc.szTooltip = _T("Send program author a mail");
            CreateHypCtrl(hwndDlg, &hc, 10, 30, 0, 0);

            return TRUE;
        }
    ...
    }
}
...


3. Tutorial: How to use the callback feature

You should have read the first tutorial before starting with this one

If you want to call a function in your program instead of opening a link when the hyperlink control was clicked you should use callbacks. First we need to define a function used as callback. This function requires one argument, the ID of the calling hyperlink control.

void HypCtrlCallback(DWORD HypCtrlID);

In that function we can differentiate various hyperlink controls and perform some action when the control was clicked. In that example we're opening a message box.

void HypCtrlCallback(DWORD HypCtrlID)
{
    switch(HypCtrlID) {
        case 1:
            MessageBox(NULL, _T("Callback Test 1 clicked!"), _T("Test"), MB_OK);
            break;
        case 2:
            MessageBox(NULL, _T("Callback Test 2 clicked!"), _T("Test"), MB_OK);
            break;
    }
}


Now it's time to tell the hyperlink control that it should use a callback function. First we define a variable of type HYPCTRL and initialize this variable with InitHypCtrl(). We fill out the cbFn, cbID and szText members of the HYPCTRL structure and create the control. The szLink member is ignored when a callback function is used. The complete code for creating two hyperlink controls with callback support looks like the following:

...
#include "hyperlink.h"
...
void HypCtrlCallback(DWORD HypCtrlID);
...
void HypCtrlCallback(DWORD HypCtrlID)
{
    switch(HypCtrlID) {
        case 1:
            MessageBox(NULL, _T("Callback Test 1 clicked!"), _T("Test"), MB_OK);
            break;
        case 2:
            MessageBox(NULL, _T("Callback Test 2 clicked!"), _T("Test"), MB_OK);
            break;
    }
}

static LRESULT CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG: {
            HYPCTRL hc;

            InitHypCtrl(&hc);
            hc.szText = _T("Callback Test 1");
            hc.cbFn = HypCtrlCallback;
            hc.cbID = 1;
            CreateHypCtrl(hwndDlg, &hc, 10, 10, 0, 0);

            InitHypCtrl(&hc);
            hc.szText = _T("Callback Test 2");
            hc.cbFn = HypCtrlCallback;
            hc.cbID = 2;
            CreateHypCtrl(hwndDlg, &hc, 10, 30, 0, 0);
            return TRUE;
        }
    ...
    }
}
...

It is also possible to use multiple callback functions by assigning the cbFn member of the HYPCTRL structure different functions.


4. Remarks

If you want to use the hyperlink control with Windows 95 you should modify "hyperlink.c" to load a hand cursor from your resource section. This could look like this:

BOOL InitHypCtrl(HYPCTRL* pHc)
{
...
        wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDR_CUR_HAND));
...
}

Other restrictions should not exist.