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:

ValueWhat the user seesEditing
WysiwygDesignRendered HTML with the caret and spell-check underline.Full WYSIWYG editing.
HtmlEditThe raw HTML source in a monospace text area.The user edits markup directly. Formatting commands targeted at a rendered DOM are no-ops here.
ReadOnlyPreviewRendered 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;    // unlock

Allowing 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.

Last updated on May 14, 2026