dotnet/wpf · error · InvalidOperationException

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

Error message

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

What it means

Trigger.Property has a setter that throws InvalidOperationException once the Trigger has been sealed (attached to a sealed Style/template). WPF seals triggers when the owning style is sealed so shared style state cannot be mutated. This protects thread-safety and consistency of sealed styles.

Solutions

  1. Create a new Trigger instance and set Property on it, then rebuild/replace the style before applying it.
  2. Build the complete Trigger (Property, Value, Setters) before adding it to Style.Triggers.
  3. If the style must change at runtime, construct a new Style with the modified trigger and assign it.

Example fix

// before
sealedTrigger.Property = UIElement.OpacityProperty; // throws
// after
var trigger = new Trigger { Property = UIElement.OpacityProperty, Value = 0.5 };
style.Triggers.Add(trigger);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

static bool IsEditable(Trigger t) => t != null && !t.IsSealed;

Try / catch

try { trigger.Property = p; }
catch (InvalidOperationException) { var replacement = new Trigger { Property = p, Value = trigger.Value }; style.Triggers[style.Triggers.IndexOf(trigger)] = replacement; }

Prevention

When it happens

Trigger: Setting trigger.Property on a Trigger instance that was already used in a Style whose resources were sealed, e.g. modifying a Style.Triggers entry after Style.Seal() or after the style is applied/frozen.

Common situations: Mutating a style's triggers at runtime after the style was applied to controls; reusing one Trigger object across styles and editing it later.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Trigger.cs:39

        [Ambient]
        [Localizability(LocalizationCategory.None, Modifiability = Modifiability.Unmodifiable, Readability = Readability.Unreadable)] // Not localizable by-default
        public DependencyProperty Property
        {
            get
            {
                // Verify Context Access
                VerifyAccess();

                return _property;
            }
            set
            {
                // Verify Context Access
                VerifyAccess();

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

                _property = value;
            }
        }

        /// <summary>
        ///     Value of the condition (equality check)
        /// </summary>
        [DependsOn("Property")]
        [DependsOn("SourceName")]
        [Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] // Not localizable by-default
        [TypeConverter(typeof(SetterTriggerConditionValueConverter))]
        public object Value
        {
            get
            {
                // Verify Context Access

View on GitHub (pinned to 81131a70a4)