dotnet/wpf · error · InvalidOperationException

SR.Format(SR.NullPropertyIllegal, "TargetType")

Error message

SR.Format(SR.NullPropertyIllegal, "TargetType")

What it means

Seal() validates the Style before freezing it for use. If TargetType was never set (null), Seal throws InvalidOperationException because a sealed style must know which element type it targets to support property validation and lookup.

Solutions

  1. Set TargetType in the Style constructor: new Style(typeof(MyControl)).
  2. Add TargetType="{x:Type ns:MyControl}" to the XAML Style declaration.
  3. Validate the style (TargetType != null) before applying it or calling Seal().

Example fix

// before
var style = new Style();
style.Setters.Add(new Setter(Control.ForegroundProperty, Brushes.Red));
style.Seal(); // throws
// after
var style = new Style(typeof(Button));
style.Setters.Add(new Setter(Control.ForegroundProperty, Brushes.Red));
style.Seal();
Defensive patterns

Strategy: validation

Validate before calling

if (style.TargetType == null)
    throw new InvalidOperationException("Set TargetType before sealing/applying the style.");
style.Seal();

Type guard

static bool IsReadyToSeal(Style s) => s != null && s.TargetType != null;

Try / catch

try { style.Seal(); }
catch (InvalidOperationException ex) { log.Error("Style missing TargetType", ex); }

Prevention

When it happens

Trigger: Calling style.Seal() explicitly, or applying a style whose TargetType was never assigned (e.g. new Style() with no argument, or XAML <Style> without TargetType being put into use).

Common situations: Creating styles programmatically and forgetting TargetType; XAML styles relying on TargetType inference that fail when used from code; dynamically built styles in helpers/tests that are applied without setting TargetType.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Style.cs:493

        /// <summary>
        /// This Style and all factories/triggers are now immutable
        /// </summary>
        public void Seal()
        {
            // Verify Context Access
            VerifyAccess();

            // 99% case - Style is already sealed.
            if (_sealed)
            {
                return;
            }

            // Most parameter checking is done as "upstream" as possible, but some
            //  can't be checked until Style is sealed.
            if (_targetType == null)
            {
                throw new InvalidOperationException(SR.Format(SR.NullPropertyIllegal, "TargetType"));
            }

            if (_basedOn != null)
            {
                if(DefaultTargetType != _basedOn.TargetType &&
                    !_basedOn.TargetType.IsAssignableFrom(_targetType))
                {
                    throw new InvalidOperationException(SR.Format(SR.MustBaseOnStyleOfABaseType, _targetType.Name));
                }
            }

            // Seal setters
            _setters?.Seal();

            // Seal triggers
            _visualTriggers?.Seal();

            // Will throw InvalidOperationException if we find a loop of

View on GitHub (pinned to 81131a70a4)