Override toolbar button properties and click behavior

Jacob builds the runbook authoring tool at a managed-services provider. Engineers use his app to draft, version, and ship the troubleshooting runbooks their NOC team follows at three in the morning. Runbooks are HTML documents with screenshots, command snippets, and tables of escalation contacts. The editor is WinFormHtmlEditor.

The annoyance that's been on Jacob's desk for a quarter is the Save button. When an engineer clicks the toolbar's Save icon, the editor pops a standard SaveFileDialog - the OS file picker. That made sense in 2014. In 2026 Jacob's runbooks are not files on disk; they are versioned documents in his app's SQL backend, with audit trails, peer-review requirements, and a custom commit-message dialog. He needs the editor's Save button to call his pipeline, not show a file picker.

While he is at it, he also wants the disk icon on the Save button to match the rest of his app's visual language, and he wants the tooltip to say "Commit runbook (Ctrl+S)" instead of the generic "Save".

Layer 1: Jacob fixes the icon and tooltip at design time

Every built-in toolbar item is exposed as a typed ToolStripButton property on WinFormHtmlEditor. Jacob selects the editor on his runbook form, walks the Document Outline, and finds BtnSave. It is a real ToolStripButton, so the Properties window shows Image, Text, ToolTipText, Visible, Enabled, DisplayStyle, Alignment - the full set. He drops in his own 16x16 commit icon and changes the tooltip text to "Commit runbook (Ctrl+S)". The designer persists both.

Jacob editing BtnSave's Image and ToolTipText at design time

Layer 2: runtime tweaks for state-dependent UI

Some of Jacob's state has to live in code. The Save button should be disabled when the runbook hasn't changed, hidden entirely when the engineer is in read-only review mode, and re-enabled after every keystroke. He reaches the same button from code:

private void RunbookForm_Load(object sender, EventArgs e)

{

    htmlEditor1.BtnSave.Visible = !isReviewMode;

    htmlEditor1.BtnSave.Enabled = false;



    htmlEditor1.HtmlChanged += (s, a) => htmlEditor1.BtnSave.Enabled = true;



    // Hide Paste-From-Word for engineers without an Office license.

    htmlEditor1.BtnPasteFromMsWord.Visible = userHasOfficeLicense;

}

Every built-in button is reachable the same way. The full set Jacob has on tap: BtnNew, BtnOpen, BtnSave, BtnCut, BtnCopy, BtnPaste, BtnPasteFromMsWord, BtnBold, BtnItalic, BtnUnderline, BtnFormatReset, BtnFormatUndo, BtnFormatRedo, BtnPrint, BtnSpellCheck, BtnSearch, BtnHighlightColor, BtnFontColor, BtnHyperlink, BtnImage, BtnInsertYouTubeVideo, BtnTable, BtnSymbol, BtnHorizontalRule, BtnOrderedList, BtnUnOrderedList, BtnAlignLeft, BtnAlignCenter, BtnAlignRight, BtnOutdent, BtnIndent, BtnStrikeThrough, BtnSuperScript, BtnSubscript, BtnBodyStyle. Combos: CmbFontName, CmbFontSize, CmbTitleInsert.

Layer 3: actually replacing the click behavior

The icon and tooltip changes only made the button look different. The click still opens the OS file picker - that is what Jacob really needs to replace. He subscribes to ToolbarItemOverrider.SaveButtonClicked. The rule, baked into every override handler in the helper class, is that when a customer handler is attached, the editor's default action does not run - your code does instead.

private void RunbookForm_Load(object sender, EventArgs e)

{

    htmlEditor1.ToolbarItemOverrider.SaveButtonClicked += (s, e) =>

    {

        // Skip the OS file picker entirely. Run the company commit pipeline.

        var commitMessage = CommitMessageDialog.ShowDialog(htmlEditor1);

        if (commitMessage == null) return;



        var html = htmlEditor1.Content.GetDocumentHtml();

        RunbookRepository.Commit(currentRunbookId, html, commitMessage, currentUser);



        htmlEditor1.BtnSave.Enabled = false;   // re-enabled on next HtmlChanged

    };

}

Jacob's commit-message dialog replacing the built-in Save File Dialog via ToolbarItemOverrider.SaveButtonClicked

The toolbar button still looks like a Save button (Jacob's icon, Jacob's tooltip). The keyboard shortcut still fires the same handler. But the action is his commit pipeline - audit trail, peer-review queue, SQL backend.

The full override menu

Jacob ends up overriding three buttons total: Save (commit pipeline), New (clear-with-confirmation), and Print (render-to-PDF service). The same pattern works for everything else. Events open for override on ToolbarItemOverrider include BoldButtonClicked, ItalicButtonClicked, UnderlineButtonClicked, ImageButtonClicked, HyperLinkButtonClicked, TableInsertButtonClicked, NewButtonClicked, OpenButtonClicked, SaveButtonClicked, PrintButtonClicked, SpellCheckButtonClicked, SearchButtonClicked, SymbolInsertButtonClicked, YouTubeVideoInsertButtonClicked, plus the combo-value-changed events FontNameComboValueChanged, FontSizeComboValueChanged, and TitleInsertComboValueChanged. The complete set lives in ToolbarItemOverrideHelper.

Picking the right layer

  • Just change icon, tooltip, or visibility? Edit the BtnXxx property in the designer or at runtime.
  • Add work alongside the editor's action - audit log, telemetry, validation? Subscribe to ToolbarItemOverrider.XxxButtonClicked and call the matching Formatting/Content/Editor service yourself afterward.
  • Replace the action entirely with your own dialog or pipeline? Subscribe and do not call the original service.
Last updated on May 15, 2026