Building a Custom Toolbar for the WPF HTML Editor

Yuki works at a Japanese ERP vendor in Osaka. Her team has just landed a redesign of the Sales Order module, and one of the visible pieces is a small notes panel that sits inside every order line. The product owner is firm: the notes panel must feel embedded, not bolted on. A full WYSIWYG toolbar with thirty buttons looming over a four-line text field is exactly the kind of thing he wants to avoid.

The requirement is clean: four commands only - Bold, Italic, Bullet List, and Hyperlink. Same height as the surrounding form labels. Same accent color as the rest of the new design system. Everything else should disappear.

Compact four-button WPF HTML Editor toolbar embedded inside a single ERP sales-order row, replacing the full factory toolbar for a tight-fitting host.

Yuki has been using the WpfHtmlEditor for the document-editor screen elsewhere in the product, so she knows it has powerful built-in toolbars. The challenge is that for this embedded scenario she doesn't want any of them visible. She digs into the API and finds two things she needs: the built-in Toolbar1 and Toolbar2 are public properties on the editor (so she can collapse them), and every individual factory button is reachable through editor.ToolbarItemOverrider.ToolbarItems (so she can re-parent the four she wants).

Her XAML defines her own bare ToolBar directly above the editing surface, sized exactly to her design tokens.

<Grid>

    <Grid.RowDefinitions>

        <RowDefinition Height="Auto" />

        <RowDefinition Height="*" />

    </Grid.RowDefinitions>

    <ToolBar x:Name="CompactToolbar" Grid.Row="0" Height="28"

             Background="{StaticResource Form.Accent}" />

    <wpfHtmlEditor:WpfHtmlEditor x:Name="NotesEditor" Grid.Row="1"

                                 Loaded="NotesEditor_OnLoaded" />

</Grid>

The Loaded handler is where the actual surgery happens. Yuki collapses both built-in toolbars and then moves only the four buttons she needs into CompactToolbar.

private void NotesEditor_OnLoaded(object sender, RoutedEventArgs e)
{
    NotesEditor.Toolbar1.Visibility = Visibility.Collapsed;
    NotesEditor.Toolbar2.Visibility = Visibility.Collapsed;
    var items = NotesEditor.ToolbarItemOverrider.ToolbarItems;
    Adopt(items.Bold);
    Adopt(items.Italic);
    Adopt(items.UnOrderedList);
    Adopt(items.Hyperlink);
}

private void Adopt(Control factoryControl)
{
    if (factoryControl.Parent is ItemsControl previousHost)
        previousHost.Items.Remove(factoryControl);
    CompactToolbar.Items.Add(factoryControl);
}
Private Sub NotesEditor_OnLoaded(sender As Object, e As RoutedEventArgs)
    NotesEditor.Toolbar1.Visibility = Visibility.Collapsed
    NotesEditor.Toolbar2.Visibility = Visibility.Collapsed
    Dim items = NotesEditor.ToolbarItemOverrider.ToolbarItems
    Adopt(items.Bold)
    Adopt(items.Italic)
    Adopt(items.UnOrderedList)
    Adopt(items.Hyperlink)
End Sub

Private Sub Adopt(factoryControl As Control)
    Dim previousHost As ItemsControl = Nothing
    If CSharpImpl.__Assign(previousHost, TryCast(factoryControl.Parent, ItemsControl)) IsNot Nothing Then previousHost.Items.Remove(factoryControl)
    CompactToolbar.Items.Add(factoryControl)
End Sub

Private Class CSharpImpl
    <System.Obsolete("Please refactor calling code to use normal Visual Basic assignment")>
    Shared Function __Assign(Of T)(ByRef target As T, value As T) As T
        target = value
        Return value
    End Function

The buttons she moves are the actual factory ToggleButton and Button instances - not copies. Every wired click handler, every tooltip, every bit of context-sensitive state highlighting that the editor maintains internally still works. When the caret sits inside bold text, her adopted Bold toggle still lights up. The visual container changed; the wiring didn't.

WPF HTML Editor with four factory buttons reparented into a slim host ToolBar while the built-in toolbar strips are hidden via Visibility=Collapsed.

The product owner reviews the result with her at the next demo. "That is what I had in my head," he says, and signs it off. A week later he asks Yuki to do the same trick on the Customer Contacts screen, this time with five buttons. She copy-pastes the helper method, swaps the buttons in the NotesEditor_OnLoaded handler, and it's done before lunch.

  • editor.Toolbar1.Visibility and editor.Toolbar2.Visibility hide the built-in toolbar strips.
  • editor.ToolbarItemOverrider.ToolbarItems exposes every factory button so you can re-parent it into your own ToolBar.
  • Re-parented factory buttons keep their click handling and context-sensitive state intact.
Last updated on May 15, 2026