dotnet/maui · error · XamlParseException

Expected VisualStateGroup but found {parent.Parent}

Error message

Expected VisualStateGroup but found {parent.Parent}

What it means

A XamlParseException from FindTypeNameForVisualState. When resolving a single-name property inside a VisualState, the converter walks up the XAML tree to find the owning type and requires the VisualState's direct parent to be a VisualStateGroup. If that parent is any other element, it throws 'Expected VisualStateGroup but found ...'.

Source

Thrown at src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs:89

			if (bpRef == null)
				throw new BuildException(PropertyResolution, node, null, propertyName, typeRef.Name);
			return bpRef;

			static XmlType GetTargetTypeName(INode node)
			{
				var targetType = ((node as ElementNode).Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string;
				return TypeArgumentsParser.ParseSingle(targetType, node.NamespaceResolver, (IXmlLineInfo)node);
			}
		}

		static XmlType FindTypeNameForVisualState(ElementNode parent, IXmlLineInfo lineInfo, ILContext context)
		{
			//1. parent is VisualState, don't check that

			//2. check that the VS is in a VSG
			// if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup))
			if (parent.Parent is not ElementNode target || !target.XmlType.IsOfAnyType(nameof(VisualStateGroup)))
				throw new XamlParseException($"Expected {nameof(VisualStateGroup)} but found {parent.Parent}", lineInfo);

			//3. if the VSG is in a VSGL, skip that as it could be implicit
			if (   target.Parent is ListNode
				|| target.Parent is ElementNode { XmlType: XmlType xt } && xt.IsOfAnyType(nameof(VisualStateGroupList)))
				target = target.Parent.Parent as ElementNode;
			else
				target = target.Parent as ElementNode;

			//4. target is now a Setter in a Style, or a VE
			if (target.XmlType.IsOfAnyType(nameof(Setter)))
			{
				var targetType = ((target?.Parent as ElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string;
				return TypeArgumentsParser.ParseSingle(targetType, parent.NamespaceResolver, lineInfo);
			}
			else
				return target.XmlType;
		}

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Wrap <VisualState> elements inside a <VisualStateGroup>, which itself goes inside a <VisualStateGroupList> under VisualStateManager.VisualStates.
  2. Validate the nesting order: control > VisualStateManager.VisualStates > VisualStateGroupList > VisualStateGroup > VisualState > Setter.

Example fix

<!-- before -->
<VisualStateManager.VisualStates>
  <VisualState x:Name="Normal">
    <VisualState.Setters>
      <Setter Property="BackgroundColor" Value="Green" />
    </VisualState.Setters>
  </VisualState>
</VisualStateManager.VisualStates>

<!-- after -->
<VisualStateManager.VisualStates>
  <VisualStateGroup>
    <VisualState x:Name="Normal">
      <VisualState.Setters>
        <Setter Property="BackgroundColor" Value="Green" />
      </VisualState.Setters>
    </VisualState>
  </VisualStateGroup>
</VisualStateManager.VisualStates>
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: A <VisualState> element is placed directly inside something other than a <VisualStateGroup> — e.g. directly under a VisualStateManager, a Style, or a control — so the parent check fails.

Common situations: Omitting the VisualStateGroup wrapper; mis-nesting VisualState inside VisualStateGroupList without the intermediate group; structural typo in a VisualStateManager block.

Related errors


AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13). Data as JSON: /api/errors/d8a8a8bd2a565d89. Report an issue: GitHub.