- Break caption into runs (plain text, link runs).
- Measure text runs with Canvas.TextWidth/TextExtent.
- Draw plain runs normally and link runs with LinkColor and underline.
- On MouseMove, compare X/Y to run rectangles and set hover state.
3. Event handling patterns
TLinkLabel supports typical VCL events. Use them to trigger navigation, open dialogs, or perform actions.
Opening a URL
A common use is opening a web page when clicked. Use ShellExecute on Windows.
Delphi example:
uses ShellAPI, Winapi.Windows; procedure TForm1.LinkLabel1Click(Sender: TObject); begin ShellExecute(0, 'open', PChar('https://example.com'), nil, nil, SW_SHOWNORMAL); end;
Wrap ShellExecute in try/except if you expect failures and consider validating the URL first.
Custom link actions
If the label represents multiple links or actions (e.g., “Terms | Privacy | Help”), parse the caption and store metadata (URLs or identifiers). In the OnClick or OnMouseUp handler, determine which part was clicked and act accordingly.
Pattern:
- Maintain an array of records {Text, Rect, ActionID}.
- On paint, compute Rect for each link text.
- On MouseUp, find which Rect contains the click and perform action associated with ActionID.
Keyboard accessibility
Make links accessible via keyboard:
- Implement OnKeyDown/OnKeyPress to react to Enter or Space when the label has focus.
- Set TabStop := True and adjust FocusControl if needed.
Example:
procedure TForm1.LinkLabel1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_RETURN then LinkLabel1Click(Sender); end;
4. Behavioral customizations
Beyond visuals and events, you may want to adjust behavior—how links track visited state, how they respond to disabled state, or how they integrate with application navigation.
Managing visited state
- Use VisitedLinkColor to show visited links.
- Track visited status per link (boolean flags) and update drawing accordingly.
- Persist visited state (registry, INI, or app settings) if meaningful across runs.
Example:
VisitedLinks['https://example.com'] := True; Invalidate; // triggers repaint to show visited color
Conditional enabling
Enable/disable the link based on application state (e.g., user login). When disabled:
- Set Enabled := False to prevent interaction.
- Optionally change color to clGrayText and Cursor to crDefault.
Multi-link labels
For labels containing multiple actionable items, prefer a control that supports link ranges (TLabel descendants or TRichEdit with links), or implement click-hit testing as described earlier.
5. Accessibility and usability
Make sure interactive text is accessible:
- Use sufficient contrast for link colors.
- Provide focus rectangle or alternative visual focus cue for keyboard users.
- Expose descriptive captions (avoid “Click here”; instead use “Open documentation — Learn more about TLinkLabel”).
- If links open external resources, indicate that (e.g., add an icon or text “(opens in new window)”).
6. Troubleshooting common issues
- Click not firing: ensure Enabled = True, Cursor set, and no overlaying control blocks mouse events.
- Incorrect hit testing in owner-draw: remeasure text after any font or DPI change.
- Colors not applying: confirm you’re changing the correct property for your TLinkLabel implementation (some skins/themes override colors).
- ShellExecute failing: ensure the URL is valid and escaped; use Windows API error codes to diagnose.
7. Example: Multi-action link label (simple implementation)
This example shows how to create a two-link label like “Terms | Privacy” by splitting caption and using rectangles for hit testing.
type TLinkPart = record Text: string; Rect: TRect; URL: string; end; procedure TForm1.FormCreate(Sender: TObject); begin // Setup font/appearance LinkLabel1.Transparent := True; LinkLabel1.Font.Name := 'Segoe UI'; // Prepare parts SetLength(FParts, 2); FParts[0].Text := 'Terms'; FParts[0].URL := 'https://example.com/terms'; FParts[1].Text := 'Privacy'; FParts[1].URL := 'https://example.com/privacy'; UpdateLinkRects; end; procedure TForm1.UpdateLinkRects; var i, x: Integer; sz: TSize; begin x := 0; for i := 0 to High(FParts) do begin Canvas.Font := LinkLabel1.Font; sz := Canvas.TextExtent(FParts[i].Text); FParts[i].Rect := Rect(x, 0, x + sz.cx, sz.cy); Inc(x, sz.cx + Canvas.TextWidth(' | ')); // spacing end; end; procedure TForm1.LinkLabel1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var i: Integer; begin for i := 0 to High(FParts) do if PtInRect(FParts[i].Rect, Point(X, Y)) then begin ShellExecute(0, 'open', PChar(FParts[i].URL), nil, nil, SW_SHOWNORMAL); Break; end; end;
8. Integration with styling frameworks and high-DPI
- If your application uses a styling engine (VCL styles, third-party skins), test link colors and fonts under each style—some styles may override component colors.
- For high-DPI, use Canvas.TextExtent and scaled fonts; avoid hard-coded pixel values. Consider using DPI-aware units and call UpdateLinkRects on DPI change.
9. When to extend vs. replace
- Extend: small tweaks (colors, hover underline, single URL) — use properties/events.
- Replace: complex needs (inline icons, multiple links, rich formatting) — use TRichEdit with link support, TWebBrowser embedded, or create a custom owner-draw component.
10. Quick checklist before shipping
- Link colors and contrast are accessible.
- Keyboard users can focus and activate links.
- Hover and click affordances are clear (cursor, underline).
- Visited-state behavior is consistent and optionally persisted.
- Behavior verified under different DPI and style settings.
- External links validated and opened safely.
Customizing TLinkLabel lets you present interactive text that feels native, accessible, and integrated with your app’s design. Use the component’s built-in properties for simple cases and owner-draw or richer controls when you need finer control over visuals and interaction.
Leave a Reply