dotnet/wpf · error · InvalidOperationException

SR.StyleTriggersCannotTargetTheTemplate

Error message

SR.StyleTriggersCannotTargetTheTemplate

What it means

Style-level triggers (PropertyTrigger/VisualTrigger in Style.Triggers) may only act on the container element itself; a trigger's PropertyValues may not target a named child (ChildName != SelfName). Template targeting is reserved for triggers inside a ControlTemplate, so ProcessVisualTriggers throws InvalidOperationException (SR.StyleTriggersCannotTargetTheTemplate).

Solutions

  1. Ensure every property value in the trigger has ChildName = StyleHelper.SelfName (no child targeting).
  2. Move child-targeting triggers into the ControlTemplate where the named child exists.
  3. Rebuild triggers for style use with plain property conditions only.

Example fix

// before
var trigger = new Trigger { Property = UIElement.IsMouseOverProperty, Value = true };
trigger.Setters.Add(new Setter(TextBlock.ForegroundProperty, Brushes.Red) /* child-targeted pv */);
style.Triggers.Add(trigger); // throws if propertyValue.ChildName != SelfName
// after
var trigger = new Trigger { Property = UIElement.IsMouseOverProperty, Value = true };
trigger.Setters.Add(new Setter(Control.ForegroundProperty, Brushes.Red)); // container-only
style.Triggers.Add(trigger);
Defensive patterns

Strategy: validation

Validate before calling

foreach (var t in style.Triggers.OfType<TriggerBase>())
    foreach (PropertyValue pv in /* trigger property values */ Enumerable.Empty<PropertyValue>())
        if (pv.ChildName != StyleHelper.SelfName)
            throw new InvalidOperationException("Style trigger targets a template child.");

Type guard

static bool StyleTriggerIsContainerOnly(Trigger t) => t.Setters.OfType<Setter>().All(s => s.TargetName == null);

Try / catch

try { style.Seal(); }
catch (InvalidOperationException ex) { log.Error("Style trigger targets template", ex); }

Prevention

When it happens

Trigger: A style MultiTrigger/Trigger whose Setter or condition is associated with a ChildName (from a template-derived or hand-built trigger) added to Style.Triggers, then sealed via Seal().

Common situations: Programmatically constructing Trigger objects reused between templates and styles; XAML tooling or code-gen emitting template-style triggers into Style.Triggers; deserialized triggers retaining ChildName data.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

            if (style._visualTriggers != null)
            {
                // Merge in "self" and child TriggerBase PropertyValues while walking
                // back up the tree. "Based-on" style rules are always added first
                // (lower priority)
                int triggerCount = style._visualTriggers.Count;
                for (int i = 0; i < triggerCount; i++)
                {
                    TriggerBase trigger = style._visualTriggers[i];

                    // Set things up to handle Setter values
                    for (int j = 0; j < trigger.PropertyValues.Count; j++)
                    {
                        PropertyValue propertyValue = trigger.PropertyValues[j];

                        // Check for trigger rules that act on container
                        if (propertyValue.ChildName != StyleHelper.SelfName)
                        {
                            throw new InvalidOperationException(SR.StyleTriggersCannotTargetTheTemplate);
                        }

                        TriggerCondition[] conditions = propertyValue.Conditions;
                        for (int k=0; k<conditions.Length; k++)
                        {
                            if( conditions[k].SourceName != StyleHelper.SelfName )
                            {
                                throw new InvalidOperationException(SR.Format(SR.TriggerOnStyleNotAllowedToHaveSource, conditions[k].SourceName));
                            }
                        }

                        // Track properties on the container that are being driven by
                        // the Style so that they can be invalidated during style changes
                        StyleHelper.AddContainerDependent(propertyValue.Property, true /*fromVisualTrigger*/, ref this.ContainerDependents);

                        StyleHelper.UpdateTables(ref propertyValue, ref ChildRecordFromChildIndex,
                            ref TriggerSourceRecordFromChildIndex, ref ResourceDependents, ref _dataTriggerRecordFromBinding,
                            null /*_childIndexFromChildID*/, ref _hasInstanceValues);

View on GitHub (pinned to 81131a70a4)