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