Skip to content

Platform views that extend Control without overriding MeasureOverride collapse to zero size when HorizontalAlignment is not Stretch #201

@pardont

Description

@pardont

Several platform views (SKCanvasViewControl, SKGLViewControl, PlatformGraphicsView, ProgressRingVisual) extend Avalonia.Controls.Control directly without overriding MeasureOverride. The base Control.MeasureOverride() returns default(Size), i.e. (0, 0).

This is not a problem when the MAUI virtual view uses the default HorizontalOptions="Fill" / VerticalOptions="Fill", because LayoutAlignment.Fill maps to HorizontalAlignment.Stretch, and Avalonia's ArrangeCore gives a stretched control the full parent-allocated space regardless of DesiredSize.

However, when the MAUI view sets HorizontalOptions="Start" (or Center, End), the handler maps it to HorizontalAlignment.Left (or Center, Right). In this case Avalonia's ArrangeCore clamps the final render size to DesiredSize — which is (0, 0). The control collapses to zero width/height, its Bounds become empty, and Render() returns early without drawing anything.

On the WinUI backend this does not occur because the WinUI handler for SKCanvasView does not limit the arranged size based on DesiredSize in the same way.

Affected controls

Control File
SKCanvasViewControl src/Avalonia.Controls.Maui.SkiaSharp.Views/Controls/SKCanvasViewControl.cs
SKGLViewControl src/Avalonia.Controls.Maui.SkiaSharp.Views/Controls/SKGLViewControl.cs
PlatformGraphicsView src/Avalonia.Controls.Maui/Platform/PlatformGraphicsView.cs
ProgressRingVisual (similar pattern)

Note: GifImage in the same codebase does override MeasureOverride, so it is unaffected.

Reproduction

  1. Create a custom control extending SKCanvasView that overrides MeasureOverride to return a non-zero size.
  2. Place it in a layout with HorizontalOptions="Start".
  3. Run on the Avalonia backend — the control is invisible.
  4. Run on the WinUI backend — the control renders correctly.

Minimal XAML:

<sk:SKButton Text="Primary button"
             HorizontalOptions="Start"
             ButtonColor="#512BD4"
             TextColor="White"
             HeightRequest="48" />

Root cause

In ViewHandler.GetDesiredSize(), the handler calls platformView.Measure(constraint) and reads platformView.DesiredSize. Since these controls return (0, 0) from MeasureOverride, the Avalonia layout system considers their desired size to be zero.

When ViewHandler.PlatformArrange() later calls platformView.Arrange(rect), Avalonia's ArrangeCore applies the alignment:

// Simplified from Avalonia's ArrangeCore
var size = HorizontalAlignment == Stretch
    ? finalSize
    : DesiredSize; // ← (0, 0) for these controls

This shrinks the render bounds to zero, causing Render() to early-return.

Suggested fix

Override MeasureOverride in each affected control to return the available size:

protected override Size MeasureOverride(Size availableSize)
{
    return availableSize;
}

This is safe because MAUI's layout system already positions and sizes children through CrossPlatformMeasure / CrossPlatformArrange / PlatformArrange with explicit bounds. The Avalonia alignment is essentially redundant for MAUI-managed views — the platform view just needs to fill whatever frame MAUI allocates.

Workaround (consumer-side)

As a workaround without modifying this library, the control author can force the platform view to Stretch alignment in OnHandlerChanged:

protected override void OnHandlerChanged()
{
    base.OnHandlerChanged();
    if (Handler?.PlatformView is Avalonia.Controls.Control ctrl)
    {
        ctrl.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch;
        ctrl.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch;
    }
}

This works because MAUI's PlatformArrange supplies the exact frame, and Stretch prevents ArrangeCore from shrinking it.

Environment

  • Avalonia.Controls.Maui: current main (90bcad8)
  • Avalonia: 12.0.0-rc1
  • SkiaSharp: 3.119.4-preview.1.1
  • OS: Windows 10 / Debian 11
  • Reproducible on: Avalonia desktop backend only (WinUI backend unaffected)

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions