dotnet/wpf · error · InvalidOperationException

SR.Format(SR.TextEditorCanNotRegisterCommandHandler…

Error message

SR.Format(SR.TextEditorCanNotRegisterCommandHandler, ((Type)_registeredEditingTypes[i]).Name, controlType.Name)

What it means

TextEditor.RegisterCommandHandlers maintains a static list of control types that already registered editing command handlers. If a new controlType is a superclass of an already-registered type, the same handlers would be attached twice, so the registration throws InvalidOperationException naming both types. It is a guard against duplicate/base-class handler registration.

Solutions

  1. Register only the most-derived control type once; remove the redundant registration.
  2. Check TextEditor's registered types (or guard with a static bool) before calling RegisterCommandHandlers.
  3. Move registration to a single point (e.g. static constructor of the leaf type).

Example fix

// before
TextEditor.RegisterCommandHandlers(controlType, ...); // called in both BaseControl and DerivedControl
// after
protected static bool _handlersRegistered;
if (!_handlersRegistered)
{
    TextEditor.RegisterCommandHandlers(controlType, ...);
    _handlersRegistered = true;
}
Defensive patterns

Strategy: validation

Validate before calling

if (TextEditorHelpers.IsRegistered(controlType)) return; // guard before calling RegisterCommandHandlers

Try / catch

try { TextEditor.RegisterCommandHandlers(controlType, ...); }
catch (InvalidOperationException) { /* already registered via a derived type; skip */ }

Prevention

When it happens

Trigger: A custom control deriving from (or being a base class of) a control type that already called RegisterCommandHandlers calls TextEditor.RegisterCommandHandlers for the same TextEditor instance.

Common situations: Registering editing handlers in a control hierarchy where both a base and derived control attempt registration; registering the same control type twice during app initialization or XAML loading.

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/d1c86e4206d16ca3. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextEditor.cs:325

        {
            // Check if we already registered handlers for this type
            Invariant.Assert(_registeredEditingTypes != null);
            lock (_registeredEditingTypes)
            {
                for (int i = 0; i < _registeredEditingTypes.Count; i++)
                {
                    // If controlType is or derives from some already registered class - we are done
                    if (((Type)_registeredEditingTypes[i]).IsAssignableFrom(controlType))
                    {
                        return;
                    }

                    // Check if controlType is not a superclass of some registered class.
                    // This is erroneus condition, which must be avoided.
                    // Otherwise the same handlers will be attached to some class twice.
                    if (controlType.IsAssignableFrom((Type)_registeredEditingTypes[i]))
                    {
                        throw new InvalidOperationException(
                            SR.Format(SR.TextEditorCanNotRegisterCommandHandler, ((Type)_registeredEditingTypes[i]).Name, controlType.Name));
                    }
                }

                // The class was not yet registered. Add it to the list before starting registering handlers.
                _registeredEditingTypes.Add(controlType);
            }

            // Mouse
            TextEditorMouse._RegisterClassHandlers(controlType, registerEventListeners);
            if (!readOnly)
            {
                // Typing
                TextEditorTyping._RegisterClassHandlers(controlType, registerEventListeners);
            }
            // Drag-and-drop
            TextEditorDragDrop._RegisterClassHandlers(controlType, readOnly, registerEventListeners);
            // Cut-Copy-Paste

View on GitHub (pinned to 81131a70a4)