dotnet/wpf · error · InvalidOperationException
Microsoft.Windows.Controls.SR.Ribbon_ContextualTabHeadersSou…
Error message
Microsoft.Windows.Controls.SR.Ribbon_ContextualTabHeadersSourceInvalid
What it means
During Ribbon template application (OnApplyTemplate path), if the RibbonContextualTabGroupItemsControl was found but has no ItemsSource, the Ribbon refuses a configuration where both ContextualTabGroupsSource is set AND the manual ContextualTabGroups collection already contains items — the two ways of supplying contextual tab groups are mutually exclusive. It throws InvalidOperationException (Ribbon_ContextualTabHeadersSourceInvalid).
Solutions
- Remove manual items from ContextualTabGroups when using ContextualTabGroupsSource.
- Or remove the ContextualTabGroupsSource binding and populate ContextualTabGroups directly.
- Choose one population strategy (source binding OR direct items) per Ribbon instance.
Example fix
// before
<Ribbon ContextualTabGroupsSource="{Binding Tabs}">
<Ribbon.ContextualTabGroups>
<RibbonContextualTabGroup Header="Tools"/>
</Ribbon.ContextualTabGroups>
</Ribbon>
// after
<Ribbon ContextualTabGroupsSource="{Binding Tabs}"/> Defensive patterns
Strategy: validation
Validate before calling
bool ok = ribbon.ContextualTabGroupsSource == null || ribbon.ContextualTabGroups.Count == 0;
if (!ok) throw new InvalidOperationException("Use either ContextualTabGroupsSource or ContextualTabGroups, not both."); Try / catch
try { ApplyTemplate(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ContextualTab")) { ribbon.ContextualTabGroups.Clear(); ApplyTemplate(); } Prevention
- Pick one population strategy per Ribbon: source binding or direct items.
- When adding ContextualTabGroupsSource, first clear ContextualTabGroups.
- Review XAML for leftover <Ribbon.ContextualTabGroups> after introducing bindings.
When it happens
Trigger: Setting ContextualTabGroupsSource (a binding to an external collection) while manually adding RibbonContextualTabGroup items to the ContextualTabGroups collection, then applying the template.
Common situations: Mixing XAML-declared contextual tab groups (<Ribbon.ContextualTabGroups>) with a ContextualTabGroupsSource binding, often after refactoring from declarative items to a source binding without clearing the collection.
Related errors
- Microsoft.Windows.Controls.SR.Format(Microsoft.Windows.Contr…
- Microsoft.Windows.Controls.SR.Format(Microsoft.Windows.Contr…
- Animation_ChildMustBeKeyFrame
- Animation_ChildMustBeKeyFrame
- Animation_NoTextChildren
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/d6d7f9749b4e4d68.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/System.Windows.Controls.Ribbon/Microsoft/Windows/Controls/Ribbon/Ribbon.cs:752
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_itemsPresenter = GetTemplateChild("ItemsPresenter") as ItemsPresenter;
_itemsPresenterPopup = this.GetTemplateChild(ItemsPresenterPopupTemplateName) as Popup;
_tabHeaderItemsControl = this.GetTemplateChild("TabHeaderItemsControl") as RibbonTabHeaderItemsControl;
if (_tabHeaderItemsControl != null && _tabHeaderItemsControl.ItemsSource == null)
{
_tabHeaderItemsControl.ItemsSource = _tabHeaderItemsSource;
}
_groupHeaderItemsControl = this.GetTemplateChild(Ribbon.ContextualTabGroupItemsControlTemplateName) as RibbonContextualTabGroupItemsControl;
if (_groupHeaderItemsControl != null && _groupHeaderItemsControl.ItemsSource == null)
{
if (ContextualTabGroupsSource != null && ContextualTabGroups.Count > 0)
{
throw new InvalidOperationException(Microsoft.Windows.Controls.SR.Ribbon_ContextualTabHeadersSourceInvalid);
}
if (ContextualTabGroupsSource != null)
{
ContextualTabGroupItemsControl.ItemsSource = ContextualTabGroupsSource;
}
else if (ContextualTabGroups != null)
{
ContextualTabGroupItemsControl.ItemsSource = ContextualTabGroups;
}
}
this.RibbonTitlePanel = this.GetTemplateChild(Ribbon.TitlePanelTemplateName) as RibbonTitlePanel;
_qatTopHost = this.GetTemplateChild(Ribbon.QatHostTemplateName) as UIElement;
_titleHost = this.GetTemplateChild(Ribbon.TitleHostTemplateName) as UIElement;
_helpPaneHost = this.GetTemplateChild(Ribbon.HelpPaneTemplateName) as UIElement;
PropertyHelper.TransferProperty(this, ContextMenuProperty); // Coerce to get a default ContextMenu if none has been specified.
PropertyHelper.TransferProperty(this, RibbonControlService.CanAddToQuickAccessToolBarDirectlyProperty);View on GitHub (pinned to 81131a70a4)