Making Your Own Buttons Context-Aware via SelectionChanged and StateQuery

    Toolbar buttons wired through ToolbarItemOverrider (see page 156) send commands to the editor, but do not automatically reflect the caret's current formatting - a Bold toggle will not appear pressed when the caret is inside bold text, and a Bullet List toggle will not reflect that the caret is inside a list. If your app uses a custom ribbon or toolbar, for example a branded set of formatting buttons in an invoice template editor or a CRM notes panel, that mismatch is confusing: the button no longer tells the user what is actually happening at the cursor. This page shows how to make custom toolbar or ribbon buttons context-aware, so each button's checked/enabled state always matches the content at the caret.

    WPF HTML Editor hosted under an Office-style ribbon; custom Bold and Italic toggles light up correctly via SelectionChanged + StateQuery as the caret moves.
    WPF HTML Editor hosted under an Office-style ribbon; custom Bold and Italic toggles light up correctly via SelectionChanged + StateQuery as the caret moves.

    Two members support this. The SelectionChanged routed event on WpfHtmlEditor fires whenever the caret moves or the selection changes. The StateQuery service answers formatting questions such as IsBold(), IsItalic(), IsUnorderedList(), and more.

    Subscribe to the event in the editor's Loaded handler, then push the returned state into your view model on each callback, as shown below.

    private void ContractEditor_OnLoaded(object sender, RoutedEventArgs e) {     ContractEditor.SelectionChanged += ContractEditor_SelectionChanged; }  private void ContractEditor_SelectionChanged(object sender, RoutedEventArgs e) {     var state = ContractEditor.StateQuery;     var vm = (RibbonViewModel)DataContext;     vm.IsBold = state.IsBold();     vm.IsItalic = state.IsItalic();     vm.IsUnderline = state.IsUnderline();     vm.IsBulletList = state.IsUnorderedList();     vm.IsNumberList = state.IsOrderedList();     vm.IsAlignLeft = state.IsJustifyLeft();     vm.IsAlignCenter = state.IsJustifyCenter();     vm.IsAlignRight = state.IsJustifyRight();     vm.ActiveFontFamily = state.GetActiveFontFamilyName();     vm.ActiveFontSize = state.GetActiveFontSize();     vm.CanUndo = state.CanUndo();     vm.CanRedo = state.CanRedo();     vm.CanPaste = state.CanPaste(); }
    Private Sub ContractEditor_OnLoaded(sender As Object, e As RoutedEventArgs)     ContractEditor.SelectionChanged += AddressOf ContractEditor_SelectionChanged End Sub  Private Sub ContractEditor_SelectionChanged(sender As Object, e As RoutedEventArgs)     Dim state = ContractEditor.StateQuery     Dim vm = CType(DataContext, RibbonViewModel)     vm.IsBold = state.IsBold()     vm.IsItalic = state.IsItalic()     vm.IsUnderline = state.IsUnderline()     vm.IsBulletList = state.IsUnorderedList()     vm.IsNumberList = state.IsOrderedList()     vm.IsAlignLeft = state.IsJustifyLeft()     vm.IsAlignCenter = state.IsJustifyCenter()     vm.IsAlignRight = state.IsJustifyRight()     vm.ActiveFontFamily = state.GetActiveFontFamilyName()     vm.ActiveFontSize = state.GetActiveFontSize()     vm.CanUndo = state.CanUndo()     vm.CanRedo = state.CanRedo()     vm.CanPaste = state.CanPaste() End Sub

    Bind the ribbon toggles in XAML to these properties, for example IsChecked="{Binding IsBold}" and IsChecked="{Binding IsItalic}". Bind Undo/Redo buttons to IsEnabled="{Binding CanUndo}" and IsEnabled="{Binding CanRedo}". As long as RibbonViewModel raises PropertyChanged on each setter, every toggle and button updates automatically as the caret moves.

    MVVM-bound ribbon above the WPF HTML Editor with Bold and Bullet List toggles becoming active as the caret enters bolded text and a bulleted list.
    MVVM-bound ribbon above the WPF HTML Editor with Bold and Bullet List toggles becoming active as the caret enters bolded text and a bulleted list.

    The StateQuery service exposes many additional context queries beyond the formatting toggles shown above, including IsStrikeThrough(), IsSubscript(), IsSuperscript(), IsHyperLink(), IsImage(), IsTable(), IsTableCell(), IsActiveOrAncestorElementHyperLink(), IsActiveOrAncestorElementTable(), GetActiveForeColor(), GetActiveHighlightColor(), GetActiveHeaderTitleNumber(), IsActiveElementHeaderTitle(), CanCut(), CanCopy(), and CanDelete(). Use these to add more ribbon features with the same event-subscription pattern.

    • Hook the routed SelectionChanged event on WpfHtmlEditor to be notified whenever the caret or selection moves.
    • Inside the handler, call editor.StateQuery.IsBold(), .IsItalic(), .IsUnorderedList(), etc. to learn the caret context.
    • Push the values into your view-model properties so any bound ToggleButton.IsChecked and IsEnabled binding updates automatically.

    Last updated on May 15, 2026

    Put this into practice.

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