Read-Only Mode and Preview Mode: Programmatic Control

    The WinForms HTML Editor exposes three operating modes through a single property on the control. Most "how do I make the editor read-only?" tickets boil down to picking the right mode and, where needed, hooking the click event so anchors still navigate.

    The three editor modes

    The mode is held in the EditorMode 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. Typing, paste, drag & drop, formatting commands all work.
    HtmlEditThe raw HTML source in a monospace textarea.The user edits markup directly. Formatting commands targeted at a rendered DOM are no-ops here.
    ReadOnlyPreviewRendered HTML exactly as a browser would show it, with no caret or selection chrome.None. Keystrokes are ignored. Hyperlinks become live (see below).

    Switching mode from C#

    Set the property at any time after the editor has loaded. The control fires EditorModeChanged after the swap is complete so you can update surrounding UI (status bar, custom ribbon, menu radio buttons) in one place.

    using SpiceLogic.HtmlEditor.Abstractions;
    
    // Switch to Preview (read-only render) on a button click:
    private void btnPreview_Click(object sender, EventArgs e)
    {
        htmlEditor1.EditorMode = EditorModes.ReadOnlyPreview;
    }
    
    // Be notified after the mode actually changes:
    htmlEditor1.EditorModeChanged += (s, e) =>
    {
        statusLabel.Text = $"Mode: {htmlEditor1.EditorMode}";
        toolbar1.Visible = htmlEditor1.EditorMode == EditorModes.WysiwygDesign;
    };
    Imports SpiceLogic.HtmlEditor.Abstractions
    
    ' Switch to Preview (read-only render) on a button click:
    Private Sub btnPreview_Click(sender As Object, e As EventArgs)
        htmlEditor1.EditorMode = EditorModes.ReadOnlyPreview
    End Sub
    
    ' Be notified after the mode actually changes:
    AddHandler htmlEditor1.EditorModeChanged, Sub(s, e)
        statusLabel.Text = $"Mode: {htmlEditor1.EditorMode}"
        toolbar1.Visible = (htmlEditor1.EditorMode = EditorModes.WysiwygDesign)
    End Sub

    The event also fires when the user clicks the built-in Source / Preview toolbar buttons, so the same handler covers both code-driven and user-driven changes.

    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 htmlEditor1.Enabled = false. That disables the surface, the toolbar and the keyboard in one step, and re-enabling restores the caret where the user left it.

    htmlEditor1.Enabled = false; // lock
    htmlEditor1.Enabled = true; // unlock
    htmlEditor1.Enabled = False ' lock
    htmlEditor1.Enabled = True ' unlock

    Link clicks in Preview

    In ReadOnlyPreview the editor renders anchors as live links, and every click is intercepted internally: the built-in preview surface always opens the href in the system default browser (see "Opening Clicked Hyperlinks in the OS Default Browser" for the background on why this changed). That handoff happens inside the preview surface itself, not through a cancellable public event - HtmlElementClicked is wired to the WysiwygDesign editing surface only, so it does not fire for a click made while EditorMode is ReadOnlyPreview. If your application needs its own logic on a link click - in-app navigation, analytics, a confirmation prompt - keep the document in WysiwygDesign mode and handle HtmlElementClicked there instead of switching to ReadOnlyPreview:

    using SpiceLogic.HtmlEditor.Abstractions;
    using SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs;
    
    htmlEditor1.HtmlElementClicked += (s, e) =>
    {
        if (htmlEditor1.EditorMode != EditorModes.WysiwygDesign)
            return;
        if (e.ElementType != HtmlElementTypes.Hyperlink)
            return;
        string href = e.ClickedElement.GetAttribute("href");
        MyAppRouter.Navigate(href); // your own handler
    };
    Imports SpiceLogic.HtmlEditor.Abstractions
    Imports SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs
    
    AddHandler htmlEditor1.HtmlElementClicked, Sub(s, e)
        If htmlEditor1.EditorMode <> EditorModes.WysiwygDesign Then Return
        If e.ElementType <> HtmlElementTypes.Hyperlink Then Return
        Dim href As String = e.ClickedElement.GetAttribute("href")
        MyAppRouter.Navigate(href) ' your own handler
    End Sub

    Because the event only fires on the editing surface, this pattern works for links the user clicks while authoring. It cannot run instead of the built-in browser handoff once the document is in ReadOnlyPreview.

    Hiding the toolbar in read-only mode

    The two factory toolbars are exposed as Toolbar1 and Toolbar2 (both ToolStrip instances). Toggle their Visible property from EditorModeChanged so the chrome follows the mode:

    htmlEditor1.EditorModeChanged += (s, e) =>
    {
        bool editing = htmlEditor1.EditorMode == EditorModes.WysiwygDesign;
        htmlEditor1.Toolbar1.Visible = editing;
        htmlEditor1.Toolbar2.Visible = editing;
        htmlEditor1.ToolbarFooter.Visible = editing;
    };
    AddHandler htmlEditor1.EditorModeChanged, Sub(s, e)
        Dim editing As Boolean = (htmlEditor1.EditorMode = EditorModes.WysiwygDesign)
        htmlEditor1.Toolbar1.Visible = editing
        htmlEditor1.Toolbar2.Visible = editing
        htmlEditor1.ToolbarFooter.Visible = editing
    End Sub

    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 on load after the editor has finished initialising:

    // On form closing:
    Properties.Settings.Default.LastEditorMode = htmlEditor1.EditorMode.ToString();
    Properties.Settings.Default.Save();
    // On form load:
    if (Enum.TryParse<EditorModes>(Properties.Settings.Default.LastEditorMode, out var saved))
        htmlEditor1.EditorMode = saved;
    ' On form closing:
    Properties.Settings.[Default].LastEditorMode = htmlEditor1.EditorMode.ToString()
    Properties.Settings.[Default].Save()
    ' On form load:
    Dim saved = Nothing
    If [Enum].TryParse(Of EditorModes)(Properties.Settings.[Default].LastEditorMode, saved) Then htmlEditor1.EditorMode = saved

    Full example - mode switch with toolbar and status sync

    using System;
    using SpiceLogic.HtmlEditor.Abstractions;
    using SpiceLogic.HtmlEditor.WinForms;
    
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            htmlEditor1.EditorModeChanged += OnEditorModeChanged;
        }
    
        private void btnPreview_Click(object sender, EventArgs e)
        {
            htmlEditor1.EditorMode = EditorModes.ReadOnlyPreview;
        }
    
        private void btnBackToDesign_Click(object sender, EventArgs e)
        {
            htmlEditor1.EditorMode = EditorModes.WysiwygDesign;
        }
    
        private void OnEditorModeChanged(object sender, EventArgs e)
        {
            bool editing = htmlEditor1.EditorMode == EditorModes.WysiwygDesign;
            htmlEditor1.Toolbar1.Visible = editing;
            htmlEditor1.Toolbar2.Visible = editing;
            statusLabel.Text = $"Mode: {htmlEditor1.EditorMode}";
        }
    }
    Imports System
    Imports SpiceLogic.HtmlEditor.Abstractions
    Imports SpiceLogic.HtmlEditor.WinForms
    
    Partial Public Class MainForm
        Inherits Form
    
        Public Sub New()
            InitializeComponent()
            AddHandler htmlEditor1.EditorModeChanged, AddressOf OnEditorModeChanged
        End Sub
    
        Private Sub btnPreview_Click(sender As Object, e As EventArgs)
            htmlEditor1.EditorMode = EditorModes.ReadOnlyPreview
        End Sub
    
        Private Sub btnBackToDesign_Click(sender As Object, e As EventArgs)
            htmlEditor1.EditorMode = EditorModes.WysiwygDesign
        End Sub
    
        Private Sub OnEditorModeChanged(sender As Object, e As EventArgs)
            Dim editing As Boolean = (htmlEditor1.EditorMode = EditorModes.WysiwygDesign)
            htmlEditor1.Toolbar1.Visible = editing
            htmlEditor1.Toolbar2.Visible = editing
            statusLabel.Text = $"Mode: {htmlEditor1.EditorMode}"
        End Sub
    End Class

    The same code path covers the two recurring tickets: the editor flips to a true read-only render, and the toolbar disappears while previewing. Give the user their own button (or the built-in Preview toolbar button) to switch back to WysiwygDesign - the editor does not forward keystrokes such as Escape out of Preview mode, since key input is ignored while EditorMode is ReadOnlyPreview.

    Ask your AI to do this

    Let your assistant do this for you. With the SpiceLogic MCP server connected, paste this into Claude Code, Cursor, or VS Code Copilot in agent mode.

    Using the SpiceLogic WinForms HTML Editor already referenced in my project, add a "Preview" toggle button to my form that switches the editor's EditorMode between WysiwygDesign for authoring and ReadOnlyPreview for reviewing a finished document, without losing the current document content on switch. Make sure hyperlink clicks still work correctly once the editor is in ReadOnlyPreview mode. Verify the exact EditorModes enum values and how EditorMode is set with the SpiceLogic MCP tools before writing any code.

    Last updated on May 14, 2026

    Put this into practice.

    .NET WinForms HTML Editor Control ships with free C# and VB.NET sample projects and a 14-day evaluation.

    Prefer a guided look? Book a free live demo with our engineers - live on Zoom or Teams, never a chatbot.