Share via

Image Measure Returns (0,0) in AutoFit Layout on Windows – Is This a Known Issue?

Muthukumar Madasamy 20 Reputation points
2025-11-18T08:30:50.2733333+00:00

Hi,

I'm encountering a platform-specific issue in .NET MAUI related to image measurement inside a custom auto-sized layout. I’ve created a custom layout called AutoFitStackLayout that uses a layout manager to measure and arrange children vertically. Here's a simplified version of the layout code:

public enum HeightMode

{

    AutoFit,

    Constrained

}

public class AutoFitStackLayout : Layout

{

    public static readonly BindableProperty HeightModeProperty = BindableProperty.Create(

        nameof(HeightMode), typeof(HeightMode), typeof(AutoFitStackLayout), HeightMode.AutoFit,

        propertyChanged: (b, o, n) => ((AutoFitStackLayout)b).InvalidateMeasure());

    public HeightMode HeightMode

    {

        get => (HeightMode)GetValue(HeightModeProperty);

        set => SetValue(HeightModeProperty, value);

    }

    public static readonly BindableProperty SpacingProperty = BindableProperty.Create(

        nameof(Spacing), typeof(double), typeof(AutoFitStackLayout), 0d,

        propertyChanged: (b, o, n) => ((AutoFitStackLayout)b).InvalidateMeasure());

    public double Spacing

    {

        get => (double)GetValue(SpacingProperty);

        set => SetValue(SpacingProperty, value);

    }

    protected override ILayoutManager CreateLayoutManager()

    {

        return new AutoFitStackLayoutManager(this);

    }

    private sealed class AutoFitStackLayoutManager : ILayoutManager

    {

        private readonly AutoFitStackLayout _layout;

        public AutoFitStackLayoutManager(AutoFitStackLayout layout)

        {

            _layout = layout;

        }

        public Size Measure(double widthConstraint, double heightConstraint)

        {

            var padding = _layout.Padding;

            double horizontalPadding = padding.HorizontalThickness;

            double verticalPadding = padding.VerticalThickness;

            double availableWidthForChildren = Math.Max(0, widthConstraint - horizontalPadding);

            double computedHeight = verticalPadding;

            double computedWidth = 0;

            double childHeightConstraint = _layout.HeightMode == HeightMode.AutoFit

                ? double.PositiveInfinity

                : Math.Max(0, heightConstraint - verticalPadding);

            int visibleCount = 0;

            foreach (var child in _layout.Children)

            {

                if (child.Visibility != Visibility.Visible)

                    continue;

                var childSize = child.Measure(availableWidthForChildren, childHeightConstraint);

                computedHeight += childSize.Height;

                computedWidth = Math.Max(computedWidth, childSize.Width);

                visibleCount++;

            }

            if (visibleCount > 1)

                computedHeight += (visibleCount - 1) * _layout.Spacing;

            computedWidth += horizontalPadding;

            if (_layout.HeightMode != HeightMode.AutoFit)

            {

                computedHeight = Math.Min(computedHeight, heightConstraint);

            }

            if (_layout.HeightMode == HeightMode.AutoFit)

            {

                _layout.HeightRequest = computedHeight;

            }

            return new Size(computedWidth, computedHeight);

        }

        public Size ArrangeChildren(Rect bounds)

        {

            double x = bounds.X + _layout.Padding.Left;

            double y = bounds.Y + _layout.Padding.Top;

            double contentWidth = Math.Max(0, bounds.Width - _layout.Padding.HorizontalThickness);

            foreach (var child in _layout.Children)

            {

                if (child.Visibility != Visibility.Visible)

                    continue;

                var desired = child.DesiredSize;

                double childHeight = desired.Height;

                var rect = new Rect(x, y, contentWidth, childHeight);

                child.Arrange(rect);

                y += childHeight + _layout.Spacing;

            }

            double totalHeight = Math.Max(bounds.Height, y - bounds.Y + _layout.Padding.Bottom - _layout.Spacing);

            return new Size(bounds.Width, totalHeight);

        }

    }

}
  • Issue: When I use only an Image inside this layout, the measure call consistently returns (0,0) only on Windows. On other platforms like Android and iOS, the image eventually measures correctly after a few calls.
  • Workaround I find: InvalidatedMeasure after Loaded the Layout.
  • Questions:
    • Is this a known issue with image measurement in MAUI on Windows?
    • Is there a recommended workaround to ensure the image is measured correctly on the first pass?
    • Could this be related to how Windows handles image loading or layout invalidation?

Any insights or suggestions would be greatly appreciated!

Developer technologies | .NET | .NET Multi-platform App UI
0 comments No comments

Answer recommended by moderator

Jack Dang (WICLOUD CORPORATION) 18,970 Reputation points Microsoft External Staff Moderator
2025-11-19T07:33:35.0933333+00:00

Hi @Muthukumar Madasamy ,

Thanks for reaching out.

When I use only an Image inside this layout, the measure call consistently returns (0,0) only on Windows. On other platforms like Android and iOS, the image eventually measures correctly after a few calls.

The official document has state that: "Each platform handles layout slightly differently. However, .NET MAUI's cross-platform layout process aims to be as platform-agnostic as possible."

For more information, you can check this link: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/layouts/custom?view=net-maui-9.0#layout-process

Below are answers to your questions:

“Is this a known issue with image measurement in MAUI on Windows?”

When the custom layout calls Measure() on an Image, WinUI has often not finished loading or decoding the image source yet, so the native control has no size information available during the initial measure pass. As a result, MAUI reports (0,0).

Android and iOS decode images earlier in the lifecycle, so this issue rarely appears on those platforms.

https://github.com/dotnet/maui/issues/7531

https://github.com/dotnet/maui/issues/4880

https://github.com/dotnet/maui/issues/26094

“Is there a recommended workaround to ensure the image is measured correctly on the first pass?”

Yes. The practical workaround is to trigger a second measure pass after the image has either loaded or had its source assigned. This ensures the custom layout measures the image at a point when it actually has a valid size.

A common approach is to invalidate the layout during the Loaded event:

image.Loaded += (_, _) => layout.InvalidateMeasure();

Note: The official documentation for VisualElement.Loaded notes that it may occur before the element has been measured, so it cannot guarantee final sizing on its own. However, in real-world cases - especially on Windows with asynchronous image loading - this event is still useful because it fires after the native control is created, allowing the subsequent measure pass to capture the correct size once decoding completes.

Some developers also listen for source changes to trigger an updated layout:

image.PropertyChanged += (s, e) =>
{
    if (e.PropertyName == nameof(Image.Source))
        layout.InvalidateMeasure();
};

This uses public APIs only and avoids relying on undocumented internal events. For reference, see the MAUI Image control documentation which explains image loading behavior and IsLoading.

“Could this be related to how Windows handles image loading or layout invalidation?”

Yes. This behavior is directly caused by how the Windows platform handles asynchronous image loading and layout invalidation inside WinUI. Specifically:

  • Images are loaded and decoded asynchronously on Windows.
  • The first layout pass usually occurs before the image has finished decoding.
  • The WinUI Image control does not automatically trigger a second layout pass for custom layouts.
  • As a result, the initial Measure() returns (0,0) until you explicitly invalidate measure from your code.

You can also reference the MAUI Image element interface documentation which explains how source changes propagate internally.

This is why custom layouts that depend on accurate child measurements need an explicit re-measure trigger on Windows.

If your issue persists, you can create a new issue on the official MAUI GitHub repository: https://github.com/dotnet/maui/issues

Hope this helps! If my answer was helpful - kindly follow the instructions here so others with the same problem can benefit as well.

Was this answer helpful?

1 person found this answer helpful.
0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.