Open links in the default browser
The editor opens a clicked link in the user's default browser or mail app: a click in preview, Ctrl+Click while editing. Handle HtmlElementClicked only when you want your own links to open your own screens.
Built in: no code needed
Customers want a clicked hyperlink to open in the user's own default browser, not inside the editor. The editor already does that in two places:
- While editing (
EditorModes.WysiwygDesign): a plain click places the caret in the link so the user can change it, and Ctrl+Click opens the link in the default browser. - In preview (
EditorModes.ReadOnlyPreview): every click opens the link in the default browser, and the editor stays on your document.
Only web (http, https) and mail (mailto) addresses are opened. Other addresses, such as file: paths or custom protocols, are never handed to Windows, because a document can name a program there, and Ctrl+Click follows the same rule.
In EditorModes.HtmlEdit the user edits raw text, so there is nothing to click. Write code only when you want different behaviour on the editing surface, for example opening a link with a plain click, or routing your own app links to your own screens. HtmlElementClicked is raised for clicks on the editing surface only; it does not fire in preview mode, where the built-in handoff to the default browser applies.
The hook: HtmlElementClicked + FireHtmlElementClickEventFor
The editor exposes a CLR event HtmlElementClicked on WinFormHtmlEditor whose args (SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs.HtmlElementClickedEventArgs) carry the clicked DOM element as ClickedElement (a System.Windows.Forms.HtmlElement), the underlying HtmlElementEventArgs as EventData, and a strongly-typed ElementType of enum HtmlElementTypes (Hyperlink, Image, Table, etc.). To make the event fire only for hyperlink clicks and skip the noise from spans, divs, and paragraphs, set htmlEditor1.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink;.
The snippet below makes a plain click on a link open it while the user edits (by default that takes Ctrl+Click). Inside the handler, cancel the default click handling and launch the URL via System.Diagnostics.Process.Start with UseShellExecute = true. UseShellExecute = true is the part that matters - it tells Windows to resolve the URL through the registered protocol handler for http / https, which is the user's OS default browser.
Full WinForms snippet
using System;
using System.Diagnostics;
using SpiceLogic.HtmlEditor.Abstractions;
using SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs;
// In your form Load handler:
htmlEditor1.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink;
htmlEditor1.HtmlElementClicked += HtmlEditor1_HyperlinkClicked;
private void HtmlEditor1_HyperlinkClicked(object sender, HtmlElementClickedEventArgs e)
{
// Raised on the editing surface: open the link with a plain click while editing.
if (htmlEditor1.EditorMode != EditorModes.WysiwygDesign)
return;
if (e.ElementType != HtmlElementTypes.Hyperlink || e.ClickedElement == null)
return;
string href = e.ClickedElement.GetAttribute("href");
if (!TryGetSafeExternalUrl(href, out string safeUrl))
return;
if (e.EventData != null)
e.EventData.ReturnValue = false; // cancel in-host nav
// UseShellExecute = true routes through the OS default browser.
Process.Start(new ProcessStartInfo { FileName = safeUrl, UseShellExecute = true });
}
private static bool TryGetSafeExternalUrl(string href, out string url)
{
url = null;
if (string.IsNullOrWhiteSpace(href))
return false;
if (href.StartsWith("#"))
return false; // in-page anchor
if (href.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
return false; // script injection
if (!Uri.TryCreate(href, UriKind.Absolute, out Uri parsed))
return false;
if (parsed.Scheme == Uri.UriSchemeHttp || parsed.Scheme == Uri.UriSchemeHttps || parsed.Scheme == Uri.UriSchemeMailto || parsed.Scheme == "tel")
{
url = parsed.AbsoluteUri;
return true;
}
return false;
}Imports System
Imports System.Diagnostics
Imports SpiceLogic.HtmlEditor.Abstractions
Imports SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs
' In your form Load handler:
htmlEditor1.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink
AddHandler htmlEditor1.HtmlElementClicked, AddressOf HtmlEditor1_HyperlinkClicked
Private Sub HtmlEditor1_HyperlinkClicked(sender As Object, e As HtmlElementClickedEventArgs)
' Raised on the editing surface: open the link with a plain click while editing.
If htmlEditor1.EditorMode <> EditorModes.WysiwygDesign Then Return
If e.ElementType <> HtmlElementTypes.Hyperlink OrElse e.ClickedElement Is Nothing Then Return
Dim href As String = e.ClickedElement.GetAttribute("href")
Dim safeUrl As String = Nothing
If Not TryGetSafeExternalUrl(href, safeUrl) Then Return
If e.EventData IsNot Nothing Then e.EventData.ReturnValue = False ' cancel in-host nav
' UseShellExecute = True routes through the OS default browser.
Process.Start(New ProcessStartInfo With {.FileName = safeUrl, .UseShellExecute = True})
End Sub
Private Shared Function TryGetSafeExternalUrl(href As String, ByRef url As String) As Boolean
url = Nothing
If String.IsNullOrWhiteSpace(href) Then Return False
If href.StartsWith("#") Then Return False ' in-page anchor
If href.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase) Then Return False ' script injection
Dim parsed As Uri = Nothing
If Not Uri.TryCreate(href, UriKind.Absolute, parsed) Then Return False
If parsed.Scheme = Uri.UriSchemeHttp OrElse parsed.Scheme = Uri.UriSchemeHttps OrElse
parsed.Scheme = Uri.UriSchemeMailto OrElse parsed.Scheme = "tel" Then
url = parsed.AbsoluteUri
Return True
End If
Return False
End FunctionWPF variant (same idea, different event-args)
If you also ship the WPF control, the same pattern applies on WpfHtmlEditor. The differences: the event args type is SpiceLogic.HtmlEditor.WPF.EditorEventArgs.HtmlElementClickedEventArgs (a RoutedEventArgs), ClickedElement is an mshtml.IHTMLElement so you read the href via e.ClickedElement.getAttribute("href", 0) as string, and to cancel the default you set e.EventData.returnValue = false;. WPF preview mode opens links in the default browser the same way. The TryGetSafeExternalUrl helper is reusable verbatim - it has no UI dependencies.
Edge cases worth handling
- Anchor jumps inside the same document (
<a href="#footnote-3">) - do not launch the browser; let the host scroll. The helper filters these out by checking for a leading#. - javascript: pseudo-URLs - never pass these to
Process.Start. They are a script-execution vector, not a navigation request. - mailto: and tel: - safe to shell-execute. Windows routes them through the user's default mail client / phone-dialer association.
- file:// URLs - the helper rejects these by default; whitelist
Uri.UriSchemeFileif you want them, but treat anyfile:href from untrusted HTML with suspicion. - Relative URLs (
about-us.html) - rejected byUriKind.Absolute; combine withhtmlEditor1.BaseUrlbefore launching if you need them resolved. - Null or empty href - the helper short-circuits on whitespace.
Security note
If the HTML your editor displays comes from untrusted input, validate the URL before calling Process.Start. The whitelist approach in TryGetSafeExternalUrl - check the parsed Uri.Scheme against an explicit allow-list of http, https, mailto, tel - is the correct pattern. Never pass a raw href from the DOM straight to Process.Start with UseShellExecute = true: a malicious document can specify shell:, vbscript:, or a custom protocol handler that runs arbitrary code on the user's machine.
What about the older "Hyperlink in default browser instead of MSIE" page?
The legacy WinForms Programming-category page describes the same idea using Internet Explorer era terminology. Prefer this page for any new code - the HtmlElementClicked + Options.FireHtmlElementClickEventFor + UseShellExecute = true pattern routes through whichever browser the user has set as their Windows default.
Ask your AI to do this
Connect the SpiceLogic MCP server, then give your coding assistant this prompt.
I use the SpiceLogic WinForms HTML Editor. Preview links and Ctrl+Click already open in the default browser. Help me make a plain click open approved links while editing, or route my own link scheme to my screens with HtmlElementClicked. Confirm the exact API names, types and defaults with the SpiceLogic MCP documentation, then show the changes for my project.