Read-Only Mode and Preview Mode: Programmatic Control
The WPF HTML Editor exposes three operating modes through a single dependency property on the control. "Make the editor read-only", "click hyperlinks while not editing" and "override the Preview toolbar button" all land on the same property + event pair.
The three editor modes
The mode is held in the EditorMode dependency property of type SpiceLogic.HtmlEditor.Abstractions.EditorModes. The enum has three values:
| Value | What the user sees | Editing |
|---|---|---|
WysiwygDesign | Rendered HTML with the caret and spell-check underline. | Full WYSIWYG editing. |
HtmlEdit | The raw HTML source in a monospace text area. | The user edits markup directly. Formatting commands targeted at a rendered DOM are no-ops here. |
ReadOnlyPreview | Rendered HTML exactly as a browser shows it, with no caret or selection chrome. | None. Keystrokes are ignored. Anchors become live (see below). |
Note on the enum name. The third value is ReadOnlyPreview. Older third-party copy sometimes refers to it as HtmlPreview; that name was never on the public enum. Use ReadOnlyPreview in code and bindings.
Switching mode from C# and XAML
From C#, set the property directly. From XAML, bind it or set the literal enum value. The control raises the EditorModeChanged routed event after the swap, so the same handler covers code-driven changes and user clicks on the built-in Source / Preview toolbar buttons.
<wpfHtmlEditor:WpfHtmlEditor
x:Name="MyEditor"
EditorMode="ReadOnlyPreview"
EditorModeChanged="MyEditor_EditorModeChanged" />Read-only without leaving Design view
If you want the editing surface to stay rendered exactly as it is - for example after Save - but to stop accepting input, do not switch modes. Set MyEditor.IsEnabled = false. That disables the surface, the toolbar and the keyboard together; re-enabling puts the caret back where the user left it.
MyEditor.IsEnabled = false; // lock
MyEditor.IsEnabled = true; // unlockAllowing link clicks in Preview
In ReadOnlyPreview the editor renders anchors as live links and opens them in the system default browser by default (see the related WinForms page "Opening a hyperlink in the default browser instead of MSIE" - the WPF control behaves the same way). To intercept the click and run your own logic, subscribe to HtmlElementClicked and read the href from the underlying MSHTML element:
using SpiceLogic.HtmlEditor.Abstractions;
using SpiceLogic.HtmlEditor.WPF.EditorEventArgs;
MyEditor.HtmlElementClicked += (s, e) =>
{
if (MyEditor.EditorMode != EditorModes.ReadOnlyPreview) return;
if (e.ElementType != HtmlElementTypes.Hyperlink) return;
string href = e.ClickedElement.getAttribute("href") as string;
MyAppRouter.Navigate(href); // your own handler
};The handler runs before the editor's built-in shell call. If you handle the URL yourself, the OS handoff effectively becomes a no-op. There is no separate "cancel" flag - doing your own work first is enough.
Hiding the toolbar in read-only mode
The two factory toolbars are exposed as Toolbar1 and Toolbar2. Toggle their Visibility from EditorModeChanged so the chrome follows the mode:
MyEditor.EditorModeChanged += (s, e) =>
{
var v = MyEditor.EditorMode == EditorModes.WysiwygDesign
? Visibility.Visible
: Visibility.Collapsed;
MyEditor.Toolbar1.Visibility = v;
MyEditor.Toolbar2.Visibility = v;
MyEditor.ToolbarFooter.Visibility = v;
};Remembering the user's last mode across sessions
Persist the enum value as its name (a string), not its integer - the int values are an implementation detail and could shift if new modes are ever added. Restore the saved value on startup:
// On window closing:
Properties.Settings.Default.LastEditorMode = MyEditor.EditorMode.ToString();
Properties.Settings.Default.Save();
// On window Loaded:
if (Enum.TryParse<EditorModes>(Properties.Settings.Default.LastEditorMode, out var saved))
MyEditor.EditorMode = saved;Full example - preview-with-link-handling and Escape to go back
<DockPanel>
<TextBlock x:Name="statusText" DockPanel.Dock="Bottom" Margin="6" />
<Button DockPanel.Dock="Top" Content="Preview" Click="OnPreviewClick" />
<wpfHtmlEditor:WpfHtmlEditor
x:Name="MyEditor"
EditorModeChanged="OnEditorModeChanged"
HtmlElementClicked="OnHtmlElementClicked"
KeyDown="OnEditorKeyDown" />
</DockPanel>using System.Windows;
using System.Windows.Input;
using SpiceLogic.HtmlEditor.Abstractions;
using SpiceLogic.HtmlEditor.WPF;
using SpiceLogic.HtmlEditor.WPF.EditorEventArgs;
public partial class MainWindow : Window
{
public MainWindow() { InitializeComponent(); }
private void OnPreviewClick(object sender, RoutedEventArgs e)
{
MyEditor.EditorMode = EditorModes.ReadOnlyPreview;
}
private void OnEditorModeChanged(object sender, RoutedEventArgs e)
{
var v = MyEditor.EditorMode == EditorModes.WysiwygDesign
? Visibility.Visible
: Visibility.Collapsed;
MyEditor.Toolbar1.Visibility = v;
MyEditor.Toolbar2.Visibility = v;
statusText.Text = $"Mode: {MyEditor.EditorMode}";
}
private void OnHtmlElementClicked(object sender, HtmlElementClickedEventArgs e)
{
if (MyEditor.EditorMode != EditorModes.ReadOnlyPreview) return;
if (e.ElementType != HtmlElementTypes.Hyperlink) return;
string href = e.ClickedElement.getAttribute("href") as string;
MyAppRouter.Navigate(href);
}
private void OnEditorKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape
&& MyEditor.EditorMode == EditorModes.ReadOnlyPreview)
{
MyEditor.EditorMode = EditorModes.WysiwygDesign;
e.Handled = true;
}
}
}The same code path covers all three recurring tickets: the editor flips to a real read-only render, the toolbar disappears, anchors stay clickable (routed through your app), and Escape brings the user back to Design without losing the document.