dotnet/maui · error · ArgumentException

Element must be of type Button.

Error message

Element must be of type Button.

What it means

MaterialButtonRenderer renders Material Design Buttons on Android. SetElement casts the incoming VisualElement to Button and throws ArgumentException if the element is not a Button. This is a hard contract: the renderer's layout, styling, and event wiring all assume a Button element.

Source

Thrown at src/Compatibility/Material/src/Android/MaterialButtonRenderer.cs:418

		AColor IBorderVisualElementRenderer.ShadowColor => ShadowColor;
		bool IBorderVisualElementRenderer.UseDefaultPadding() => OnThisPlatform().UseDefaultPadding();
		bool IBorderVisualElementRenderer.UseDefaultShadow() => OnThisPlatform().UseDefaultShadow();
		bool IBorderVisualElementRenderer.IsShadowEnabled() => true;
		VisualElement IBorderVisualElementRenderer.Element => Element;
		AView IBorderVisualElementRenderer.View => this;

		// IVisualElementRenderer
		VisualElement IVisualElementRenderer.Element => Element;
		VisualElementTracker IVisualElementRenderer.Tracker => _tracker;
		AView IVisualElementRenderer.View => this;

		SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint)
		{
			return _buttonLayoutManager.GetDesiredSize(widthConstraint, heightConstraint);
		}

		void IVisualElementRenderer.SetElement(VisualElement element) =>
			Element = (element as Button) ?? throw new ArgumentException("Element must be of type Button.");

		void IVisualElementRenderer.SetLabelFor(int? id)
		{
			if (_defaultLabelFor == null)
				_defaultLabelFor = ViewCompat.GetLabelFor(this);
			ViewCompat.SetLabelFor(this, (int)(id ?? _defaultLabelFor));
		}

		void IVisualElementRenderer.UpdateLayout() =>
			_tracker?.UpdateLayout();

		// IViewRenderer
		void IViewRenderer.MeasureExactly() =>
			ViewRenderer.MeasureExactly(this, Element, Context);

		// ITabStop
		AView ITabStop.TabStop => this;

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Verify ExportRenderer maps Button (not a custom type) to MaterialButtonRenderer.
  2. Ensure custom controls intended for this renderer derive from Button.
  3. Audit the renderer registrar for duplicate Button registrations across assemblies.
  4. Confirm `VisualMarker.Material` is applied to actual Button instances.

Example fix

// before
[assembly: ExportRenderer(typeof(MyCustomLabel), typeof(MaterialButtonRenderer))]

// after
[assembly: ExportRenderer(typeof(Button), typeof(MaterialButtonRenderer))]
Defensive patterns

Strategy: type-guard

Validate before calling

if (element is not Button)
    throw new InvalidOperationException($"Expected Button, got {element?.GetType().Name}");
renderer.SetElement(element);

Type guard

static bool IsButton(VisualElement? element) => element is Button;

Prevention

When it happens

Trigger: The renderer registry dispatches a non-Button element to MaterialButtonRenderer. Occurs with incorrect ExportRenderer mappings or when a custom element type not deriving from Button is paired with this renderer.

Common situations: Misconfigured ExportRenderer attributes pairing a non-Button element with MaterialButtonRenderer. Stale registrations left after refactoring controls. Third-party libraries registering conflicting Button renderers.

Related errors


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