dotnet/wpf · error · InvalidOperationException

SR.Format(SR.UnexpectedValueTypeForDataTrigger…

Error message

SR.Format(SR.UnexpectedValueTypeForDataTrigger, propertyValue.ValueType)

What it means

During MultiDataTrigger.Seal(), each PropertyValue in the trigger's PropertyValues list must have a ValueType of Trigger or PropertyTriggerResource so it can be remapped to the DataTrigger variants. Any other internal PropertyValueType indicates corrupted or improperly constructed setter state, so WPF throws InvalidOperationException including the unexpected value type.

Solutions

  1. Build setters via the public Setters collection (Setter objects) instead of manipulating PropertyValues directly
  2. Do not reuse PropertyValues or Setter internals created for a different trigger type (Trigger/MultiTrigger) in a MultiDataTrigger
  3. Recreate the MultiDataTrigger from scratch rather than mutating its internals after construction

Example fix

// before
var pv = new PropertyValue(PropertyValueType.PropertyTrigger, value);
trigger.PropertyValues.Add(pv); // wrong internal type
// after
trigger.Setters.Add(new Setter(TextBlock.TextProperty, value));
Defensive patterns

Strategy: validation

Validate before calling

// Only build triggers through the public API
if (trigger.Setters.Count == 0 && trigger.PropertyValuesInternalCount > 0)
    throw new InvalidOperationException("PropertyValues manipulated directly; use Setters");

Try / catch

try { style.Seal(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("value type")) {
    // rebuild the trigger via Setters collection
}

Prevention

When it happens

Trigger: Programmatically constructing a MultiDataTrigger and injecting PropertyValues/Setter objects whose internal PropertyValueType is not Trigger or PropertyTriggerResource, then sealing the style; not reachable through normal XAML parsing of <MultiDataTrigger.Setters>.

Common situations: Custom framework code or styling helpers that build PropertyValue structs directly; cloning/reusing setter internals across trigger types; low-level manipulation of the sealed style machinery.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/MultiDataTrigger.cs:132

                    LogicalOp.Equals,
                    _conditions[i].Value);
            }

            // Set conditions array for all property triggers
            for (int i = 0; i < PropertyValues.Count; ++i)
            {
                PropertyValue propertyValue = PropertyValues[i];
                propertyValue.Conditions = TriggerConditions;
                switch (propertyValue.ValueType)
                {
                    case PropertyValueType.Trigger:
                        propertyValue.ValueType = PropertyValueType.DataTrigger;
                        break;
                    case PropertyValueType.PropertyTriggerResource:
                        propertyValue.ValueType = PropertyValueType.DataTriggerResource;
                        break;
                    default:
                        throw new InvalidOperationException(SR.Format(SR.UnexpectedValueTypeForDataTrigger, propertyValue.ValueType));
                }

                // Put back modified struct
                PropertyValues[i] = propertyValue;
            }

            base.Seal();
        }

        // evaluate the current state of the trigger
        internal override bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField)
        {
            bool retVal = (TriggerConditions.Length > 0);

            for( int i = 0; retVal && i < TriggerConditions.Length; i++ )
            {
                retVal = TriggerConditions[i].ConvertAndMatch(StyleHelper.GetDataTriggerValue(dataField, container, TriggerConditions[i].Binding));
            }

View on GitHub (pinned to 81131a70a4)