Updated December 18, 2013: Source code for lblHyperlink is available here, parent class (lblLabel) source is available here.
The lblHyperlink label subclass
One of the more popular movements in desktop applications is “webification.” Users have become so used to the web interface via their browser, more and more desktop apps have added clickable hyperlinks as an important part of their UI. I’ve created a subclass of the lblLabel Visual FoxPro Label Control class to mimic a hyperlink.
This subclass of the contains the following properties which override the parent class:
MousePointer = 15 ForeColor = RGB(0,102,204) Name = "lblHyperlink"
I set the ForeColor to blue, simply for the visual cue in design time that I’m working with a hyperlink instead of a plain old label.
Plus a couple of other properties, for the ForeColor (we’ll change the ForeColor when the user hovers over the link):
nForeColor = 0 nHoverColor = 0
We’ll change the MousePointer to 15 (Hand), as a visual indication that it’s a link. I want the ForeColor of the link to be whatever Windows says the COLOR_HOTLIGHT (from GetSysColor()) should be for a hyperlink, and when the user hovers over the link I’ll change it to the COLOR_MENUHILIGHT value.
We’ll set the ForeColor (and hover color) in the Init event:
PROCEDURE Init DODEFAULT() ** Use GetSysColor to get the hyperlink color DECLARE INTEGER GetSysColor IN "user32" INTEGER nIndex This.nForeColor = GetSysColor(26) && COLOR_HOTLIGHT This.nHoverColor = GetSysColor(29) && MENU_HOTLIGHT ENDPROC
We’ll also add some code to the MouseEnter and MouseLeave events:
PROCEDURE MouseEnter LPARAMETERS nButton, nShift, nXCoord, nYCoord WITH THIS .ForeColor = .nHoverColor .FontUnderline = .T. .Caption = .Caption ENDWITH ENDPROC
We set the ForeColor to the COLOR_MENUHILIGHT color and underline the label, just to provide an additional visual cue to the user at runtime that this is a hyperlink.
Notice the .Caption = .Caption line? I’ve seen “bleeding” with some form controls (including labels from time to time, when the app is minimized then restored) which have a BackStyle = 0 (Transparent). This line corrects the problem with “bleeding.”
The MouseLeave event looks like this:
PROCEDURE MouseLeave LPARAMETERS nButton, nShift, nXCoord, nYCoord WITH THIS .ForeColor = .nForeColor .FontUnderline = .F. .Caption = .Caption ENDWITH ENDPROC
Once again we reset the ForeColor back to the COLOR_HOTLIGHT value in GetSysColor(). Then we remove the underline, and do the “bleeding” fix, just in case.
This is, of course, just basic functionality. To really make this shine, I recommend viewing Tamar Granor’s presentation “BindEvents for Better Applications” from the Online Visual FoxPro Users Group (OFUG) meeting (recorded on Tuesday, February 19, 2013).
And we’re done with the hyperlink!
Well, almost done. Since the hyperlink control is based on a Label, the link cannot get focus by using the keyboard. We need a way to be able to select (and click) the link without a mouse.
Enter, a container object.
The cntHyperlink container class
The cntHyperlink container class consists of a container object which contains the aforementioned lblHyperlink label and a CommandButton.
It is a subclass of cntContainer (shown below).
cntContainer has a custom abstract method named OnClick, which is called in the Click event:
DEFINE CLASS cntContainer AS Container Width = 194 Height = 75 BackStyle = 0 BorderWidth = 0 Name = "cntContainer" PROCEDURE onclick ** Abstract method called by the Click() event ENDPROC PROCEDURE Click This.OnClick() ENDPROC ENDDEFINE
The cntHyperlink class has a custom property named cHotKey, which you could use as a hot key for the hyperlink:
cHotKey = ""
It also contains a lblHyperlink label. It remains the same, except we’ll add the following code in the OnClick() method which will “bubble up” to the parent container:
PROCEDURE lblHyperlink.OnClick This.Parent.OnClick() ENDPROC
cntHyperlink container also has an OnClick() method, inherited from cntContainer.
We also have a CommandButton subclass added to the container. It contains the following events: LostFocus, GotFocus, and Init. It also contains a method named OnClick() (as do most of the controls in my base classes).
PROCEDURE cmdButton.LostFocus This.Parent.LostFocus() ENDPROC PROCEDURE cmdButton.GotFocus This.Parent.GotFocus() ENDPROC PROCEDURE cmdButton.Init This.Height = 0 This.Width = 0 ENDPROC PROCEDURE cmdButton.OnClick This.Parent.OnClick() ENDPROC
Since it’s a hyperlink control, I don’t want the button to be visible on the form. In the Init event, we’ll size it to zero height and zero width. The GotFocus, LostFocus, and OnClick events simply “bubble up” to the parent container.
We’re done with the label and button, so all we have left is the container itself. In the Init, we add the following code which sets the Caption of the button to the cHotKey if it has been set:
PROCEDURE Init DODEFAULT() IF !EMPTY(This.cHotKey) This.cmdButton.Caption = "<" + This.cHotKey ENDIF ENDPROC
Since we’re “bubbling up” events from the command button, we’ll add the following code to the GotFocus and LostFocus events:
PROCEDURE GotFocus WITH THIS .lblHyperlink.ForeColor = .lblHyperlink.nHoverColor .lblHyperlink.FontUnderline = .T. ENDWITH ENDPROC PROCEDURE LostFocus WITH THIS .lblHyperlink.ForeColor = .lblHyperlink.nForeColor .lblHyperlink.FontUnderline = .F. ENDWITH ENDPROC
And now we’re finally ready to place the instance of the cntHyperlink class on a form, complete with Tab Order functionality, hot key functionality, etc.
All we’ll need is to add the instance code for the container OnClick() method to handle the actual clicks on the hyperlink.