dotnet/wpf · error · InvalidOperationException

SR.Format(SR.CannotChangeAfterSealed, "EventTrigger")

Error message

SR.Format(SR.CannotChangeAfterSealed, "EventTrigger")

What it means

EventTrigger.RoutedEvent's setter throws InvalidOperationException (CannotChangeAfterSealed) if the trigger has been sealed. Sealing happens when the trigger becomes part of a Style/ControlTemplate in use, after which its configuration is immutable for thread-safety.

Solutions

  1. Set RoutedEvent before adding the trigger to the style/template
  2. Create a new EventTrigger with the desired RoutedEvent and replace it in the collection (before the style is applied)
  3. Recreate the entire Style/Template rather than mutating sealed triggers

Example fix

// before
var trig = new EventTrigger();
style.Triggers.Add(trig);
ApplyStyle();
trig.RoutedEvent = Button.ClickEvent; // throws
// after
var trig = new EventTrigger { RoutedEvent = Button.ClickEvent };
style.Triggers.Add(trig);
Defensive patterns

Strategy: validation

Validate before calling

if (trigger.IsSealed) throw new InvalidOperationException("Create a new EventTrigger instead of mutating a sealed one");

Type guard

bool CanConfigure(EventTrigger t) => !t.IsSealed;

Try / catch

try { trigger.RoutedEvent = ev; }
catch (InvalidOperationException ex) when (ex.Message.Contains("sealed")) { trigger = new EventTrigger { RoutedEvent = ev }; }

Prevention

When it happens

Trigger: Setting trigger.RoutedEvent after the EventTrigger was added to a Style.Triggers / FrameworkTemplate.Triggers collection that has been sealed.

Common situations: Mutating shared style triggers at runtime; trying to retarget an EventTrigger after the style was applied to controls; editing template triggers post-layout.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/EventTrigger.cs:96

        }
        
        /// <summary>
        ///     The Event that will activate this trigger - one must be specified
        /// before an event trigger is meaningful.
        /// </summary>
        public RoutedEvent RoutedEvent
        {
            get
            {
                return _routedEvent;
            }
            set
            {
                ArgumentNullException.ThrowIfNull(value);

                if ( IsSealed )
                {
                    throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "EventTrigger"));
                }

                // When used as an element trigger, we don't actually need to seal
                //  to ensure cross-thread usability.  However, if we *are* fixed on
                //  listening to an event already, don't allow this change.
                if( _routedEventHandler != null )
                {
                    // Recycle the Seal error message.
                    throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "EventTrigger"));
                }

                _routedEvent = value;
            }
        }

        /// <summary>
        ///     The x:Name of the object whose event shall trigger this 
        /// EventTrigger.   If null, then this is the object being Styled

View on GitHub (pinned to 81131a70a4)