dotnet/wpf · error · InvalidOperationException
SR.Format(SR.CannotChangeAfterSealed…
Error message
SR.Format(SR.CannotChangeAfterSealed, "TriggerActionCollection")
What it means
TriggerActionCollection.CheckSealed() throws InvalidOperationException (CannotChangeAfterSealed, 'TriggerActionCollection') from Clear, RemoveAt, Add, Insert, Remove and the constructor path when the collection is sealed. A trigger's action collection is sealed together with its owning trigger/style once applied, making it read-only.
Solutions
- Rebuild the Style/Trigger in a fresh instance and reassign it to the controls
- Perform all collection edits before the style is applied (before control.Style is set)
- Guard edits with a check of the trigger's IsSealed state
Example fix
// before
trigger.Actions.Add(newAction); // InvalidOperationException once style applied
// after
var newTrigger = new Trigger { Property = trigger.Property, Value = trigger.Value };
foreach (var a in trigger.Actions) newTrigger.Actions.Add(a);
newTrigger.Actions.Add(newAction);
var newStyle = new Style { TargetType = typeof(Button) };
newStyle.Triggers.Add(newTrigger);
button.Style = newStyle; Defensive patterns
Strategy: validation
Validate before calling
if (!trigger.IsSealed) { trigger.Actions.Add(newAction); } else { /* rebuild trigger/style */ } Type guard
static bool CanEditActions(TriggerBase t) => t != null && !t.IsSealed;
Try / catch
try { actions.Add(a); }
catch (InvalidOperationException ex) when (ex.Message.Contains("TriggerActionCollection")) { /* rebuild style */ } Prevention
- Complete all Actions edits before applying the style
- Prefer XAML-defined triggers over runtime edits
- Treat sealed collections as read-only; create new ones instead
When it happens
Trigger: Calling Add/Insert/Remove/Clear/RemoveAt on a trigger's Actions (or Exits) collection after the parent Style/Template was sealed by application to an element.
Common situations: Dynamically adding actions to a trigger of an in-use style; a shared template whose triggers are mutated after being applied to multiple controls.
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
- SR.Format(SR.CannotChangeAfterSealed, "trigger")
- SR.Format(SR.CannotChangeAfterSealed, "TriggerAction")
- SR.TriggerActionAlreadySealed
- CannotChangeAfterSealed
- CannotChangeAfterSealed
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/9c6dcf4ce9511490.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/TriggerActionCollection.cs:292
internal DependencyObject Owner
{
get { return _owner; }
set
{
Debug.Assert (Owner == null);
_owner = value;
}
}
///////////////////////////////////////////////////////////////////////
// Private members
// Throw if a change is attempted at a time we're not allowing them
private void CheckSealed()
{
if ( _sealed )
{
throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "TriggerActionCollection"));
}
}
// Throw if the given object isn't a TriggerAction
private TriggerAction VerifyIsTriggerAction(object value)
{
TriggerAction action = value as TriggerAction;
if ( action == null )
{
ArgumentNullException.ThrowIfNull(value);
throw new ArgumentException(SR.MustBeTriggerAction);
}
return action;
}
View on GitHub (pinned to 81131a70a4)