A Hyperlink Class: Subclassing the Label Control

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 a “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.

Subclassing Visual FoxPro Base Classes: Label Control

Updated December 16, 2013: Source code for lblLabel is available here.

The next few blog posts will look at subclassing the Visual FoxPro base classes, with an emphasis on UI design as it relates to Microsoft’s User Experience Interaction Guidelines (or UX Guide). Since the emphasis is on playing well with Windows, I’ll assume that my apps will be using Themes when available at the OS-level.

Subclassing the Visual FoxPro Label Control

Typically, I stick to the following default properties for labels:

AutoSize = .T.
BackStyle = 0 (Transparent) 
FontName = "Segoe UI" (Default for my development OS) 
FontSize = 9 (Default for my development OS)

Though I typically use “Segoe UI” as the FontName, since I’m currently using Windows 8 I’m actually going to use “Segoe UI Symbol” in the Properties window, and change it to Segoe UI in code for the class.

I use AutoSize because I may need to change the font name and/or size, depending on the version of Windows the app is running on, and I use the default font and size for the OS on my development machine. This makes form design much easier for me. I also set the label BackStyle to transparent to prevent it from having a “gray” backcolor if the label is placed on a control (or form) that is not using a “default” color.

I consider label controls to be equivalent to “text” in Microsoft-speak. As long as we’re discussing labels (and text), let’s talk a little about fonts and font sizes.

The Microsoft UX Guide contains the following recommendations for fonts and font sizes:

Operating System Font (size)
Windows Vista and later Segoe UI (9pt)
Pre-Windows Vista Tahoma (8pt)

The guide also provides (on pages 620-624) some default color information and sizes for various types of text (or Labels) you may have in the app. We will use these for some additional subclasses of our label.

The lblLabel Class

Based on this information, I’ve created a subclass of Label (named lblLabel) with the following properties:

AutoSize = .T.
FontName = "Segoe UI Symbol"
BackStyle = 0
Caption = "lblLabel"
Height = 17
Width = 43
Name = "lblLabel"

Since Segoe UI is a newer font introduced in Windows Vista, computers running Windows XP may not have this font available (even if an XP machine does have Segoe UI, it can sometimes be rendered kind of blurry on XP, so I usually stick with Tahoma for pre-Vista versions of Windows anyway).

Since the UX Guide recommends “Tahoma, 8” as the pre-Vista default, and since my Segoe UI has disappeared from my Properties window in Windows 8, I’ve added the following code to the Init() method:

** Are we running a pre-Vista version of Windows?
IF VAL(OS(3)) < 6 && Windows Pre-Vista (XP, 2000, etc)
   ** Set the default font to Tahoma. Plus,
   ** if the font size is set to the Vista
   ** default (9pt), set it to 8pt. If it is not
   ** the default, let's make it 1pt smaller.
   This.FontName = "Tahoma"
   This.FontSize = IIF(This.FontSize=9,8,This.FontSize-1)
ELSE
   ** On my Windows 8 machine, Segoe UI is not
   ** available in the Properties window, so I 
   ** had to choose Segoe UI Symbol. We'll do
   ** the switch to Segoe UI here.
   This.FontName = "Segoe UI"
   This.FontSize = 9
ENDIF

Even though users normally don’t “click” on Labels, some additional subclassed labels we’ll cover later (think hyperlinks) do have clickable interactions, so I’ve added the following code to the Click event for the label :

PROCEDURE Click
   ** Code in methods, not events!
   This.OnClick()
ENDPROC

With this, I’ve told the label to call an abstract method in the label class called OnClick. Since it’s an abstract method, we’ll leave it empty in this class, but we will use it later.

PROCEDURE OnClick
   ** Abstract method called by the Click() event
ENDPROC

And we’re all set and ready to drop an instance of our label on a form!

But, there’s some additional considerations…

Some of our subclasses of this Label control will be set to use colors other than the “Default” colors found in the Visual FoxPro Properties window. While these different-colored labels will look fine in most instances, there are times I might want the Label (or “text”) to revert back to the Windows System Color.

We’ll look at those instances in a future post.

A “Visual Cue” Textbox

This is a textbox control inspired by the Search box in Windows Explorer:

Visual cue text box in Windows Explorer

A visual cue text box in Windows Explorer

I think this type of control would be useful in several different scenarios:

  • For a Search box (as shown in the image above)
  • When you don’t have enough screen real estate for a label and textbox together
  • When you want to display the format mask for the box

We’ll start off with a basic textbox control (subclassed from the Visual FoxPro TextBox base class):

DEFINE CLASS txttextbox AS textbox
   FontName = "Segoe UI"
   Height = 23
   Width = 100
   IntegralHeight = .T.
   Name = "txttextbox"

   PROCEDURE Init
      ** Are we running a pre-Vista version of Windows?
      IF VAL(OS(3)) < 6 && Windows Pre-Vista
         ** Set the default font to Tahoma. Plus,
         ** if the font size is set to the Vista
         ** default (9pt), set it to 8pt. If it is not
         ** the default, let's make it 1pt smaller.
         This.FontName = "Tahoma"
         This.FontSize = IIF(This.FontSize=9,8,This.FontSize-1)
      ENDIF
   ENDPROC

   PROCEDURE RightClick
      DEFINE POPUP shortcut SHORTCUT RELATIVE FROM MROW(),MCOL()
      DEFINE BAR _med_cut OF shortcut PROMPT "Cu<t" ;
         KEY CTRL+X, "Ctrl+X" ;
         PICTRES _med_cut ;
         MESSAGE "Removes the selection and places it onto the Clipboard"
      DEFINE BAR _med_copy OF shortcut PROMPT "<Copy" ;
         KEY CTRL+C, "Ctrl+C" ;
         PICTRES _med_copy ;
         MESSAGE "Copies the selection onto the Clipboard"
      DEFINE BAR _med_paste OF shortcut PROMPT "<Paste" ;
         KEY CTRL+V, "Ctrl+V" ;
         PICTRES _med_paste ;
         MESSAGE "Pastes the contents of the Clipboard"
      DEFINE BAR _med_clear OF shortcut PROMPT "Cle<ar" ;
         PICTRES _med_clear ;
         MESSAGE "Removes the selection and does not place it onto the Clipboard"
      DEFINE BAR 5 OF shortcut PROMPT "-"
      DEFINE BAR _med_slcta OF shortcut PROMPT "Se

I’ll create a subclass of txtTextbox, name it txtVisualCue, and add the following properties:

cCueText = ""
lCueVisible = .F.
cForecolor = ""
lFontItalic = .F.
uOriginalValue = ""
nAlignment = 3
Name = "txtVisualCue"

We’ll store some values from the textbox to the custom properties in the Init event:

PROCEDURE Init
   DODEFAULT()
   WITH THIS
      .lFontItalic = .FontItalic
      .cForeColor = .ForeColor
      .nAlignment = .Alignment
      .uOriginalValue = .Value
      .lCueVisible = EMPTY(This.Value)
   ENDWITH
ENDPROC

The “visual cue” is driven by the lCueVisible property. We’ll assign a value to lCueVisible in the GotFocus and LostFocus events for the textbox:

PROCEDURE GotFocus
   IF This.lCueVisible
      This.lCueVisible = .F.
   ENDIF
ENDPROC
PROCEDURE LostFocus
   IF EMPTY(This.Value)
      This.lCueVisible = .T.
   ENDIF
ENDPROC

Then we’ll use the lCueVisible_Assign method to “drive” the textbox:

PROCEDURE lCueVisible_assign
   LPARAMETERS vNewVal
   THIS.lCueVisible = m.vNewVal

   WITH THIS
      IF .lCueVisible
         .ForeColor = .DisabledForeColor
         .FontItalic = .T.
         .Alignment = 3
         .Value = ALLTRIM(.cCueText)
      ELSE
         .ForeColor = .cForeColor
         .FontItalic = .lFontItalic
         .Alignment = .nAlignment

         IF ALLTRIM(TRANSFORM(.Value)) == ALLTRIM(.cCueText)
            .Value = .uOriginalValue
         ENDIF
      ENDIF
   ENDWITH
ENDPROC

In effect, whenever the Value property is EMPTY(), we’ll display the CueText in the textbox. Anytime the textbox gets focus, we’ll set lCueVisible to .F., which will update the textbox accordingly.

Admittedly, this is a pretty basic example, but this type of textbox has great potential for enhancing the UI of my Visual FoxPro applications.

Demo form with "visual cue" textbox controls

Demo form with “visual cue” textbox controls

One Ugly App… LIVE!!!

Looking at screenshots is kind of boring, and there’s only about 100 people in the world that have seen my ugly app in-person, so I decided to do a screencast to show it off.

It’s 3 minutes and 56 seconds long. If you don’t want to watch the whole video, but would like to see how to go from “Fox Ugly” to “Fox Foxy” in just two clicks, feel free to jump ahead to the 3:30 mark.

Enjoy!

For what it’s worth, I made up the fiverr story for the video (and the fiverr post is made up). If you use fiverr, no offense is intended.

If the YouTube frame isn’t shown on the page, click here to watch One Ugly App… LIVE!!!