dotnet/wpf · error · InvalidOperationException

SR.BindingGroup_CannotChangeGroups

Error message

SR.BindingGroup_CannotChangeGroups

What it means

During BindingGroup discovery, a binding expression determines which BindingGroup it belongs to by walking up to the root item. If the root element's BindingGroup differs from the BindingGroup the expression already joined (bg), the state is inconsistent and BindingExpressionBase throws this InvalidOperationException. WPF considers re-parenting an expression into a different group an unsupported operation.

Solutions

  1. Set the BindingGroup on the container/root element BEFORE bindings are established (e.g. in XAML or before children load).
  2. Do not change or move a BindingGroup while its bindings are active; create a fresh BindingGroup and re-apply bindings.
  3. If re-parenting elements, ensure the destination root's BindingGroup matches the original group the bindings joined.

Example fix

// before: group swapped after bindings activated
grid.BindingGroup = newBindingGroup; // throws for existing bindings

// after: apply group first, then re-create bindings
grid.BindingGroup = newBindingGroup;
grid.SetBinding(TextBox.TextProperty, new Binding("Value"));
Defensive patterns

Strategy: validation

Validate before calling

var rootGroup = rootElement.BindingGroup;
if (rootGroup != null && expression.BindingGroup != null && expression.BindingGroup != rootGroup)
    throw new InvalidOperationException("Binding already belongs to a different BindingGroup");

Try / catch

try { container.BindingGroup = newGroup; }
catch (InvalidOperationException ex) when (ex.Message.Contains("group"))
{
    // rebuild bindings under the new group
}

Prevention

When it happens

Trigger: Calling UpdateSource/UpdateTarget or adding a binding while the element tree is being re-parented so that the root's BindingGroup changes after the binding was grouped; programmatically reassigning a BindingGroup on a container while its bindings are already registered to a different group.

Common situations: Dynamic UI where BindingGroup is swapped or the element is moved between containers with different BindingGroups; DataTemplates reused across items with differing group state; manual code that sets BindingGroup after bindings have activated.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/c7056ee2558f33f2. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingExpressionBase.cs:1836

                if (explicitJoin)
                {
                    root.UpdateBindingGroup(bg);

                    if (bg.SharesProposedValues && TraceData.IsEnabled)
                    {
                        TraceData.TraceAndNotifyWithNoParameters(TraceEventType.Warning,
                                TraceData.SharesProposedValuesRequriesImplicitBindingGroup(
                                        TraceData.Identify(root),
                                        root.ParentBindingBase.BindingGroupName,
                                        TraceData.Identify(bg)),
                                this);
                    }
                }
            }
            else
            {
                if (root.BindingGroup != bg)
                    throw new InvalidOperationException(SR.BindingGroup_CannotChangeGroups);
            }
        }

        // mark a binding as non-grouped, so that we avoid doing the discovery again
        private void MarkAsNonGrouped()
        {
            // Leaf bindings only get asked once, so there's no need to add a mark
            if (!(this is BindingExpression))
            {
                SetValue(Feature.BindingGroup, NullBindingGroupReference);
            }
        }

        // add to, or remove from, the CommitManager's set of dirty/invalid bindings
        internal void NotifyCommitManager()
        {
            if (IsReflective && !IsDetached && !Engine.IsShutDown)
            {

View on GitHub (pinned to 81131a70a4)