diff --git a/src/Controls/src/Core/Layout/FlexLayout.cs b/src/Controls/src/Core/Layout/FlexLayout.cs
index 235c6dd5917a..1e43f5c0ee5a 100644
--- a/src/Controls/src/Core/Layout/FlexLayout.cs
+++ b/src/Controls/src/Core/Layout/FlexLayout.cs
@@ -497,22 +497,32 @@ void AddFlexItem(int index, IView child)
{
item.SelfSizing = (Flex.Item it, ref float w, ref float h) =>
{
- var sizeConstraints = item.GetConstraints();
+ Size request;
- sizeConstraints.Width = (InMeasureMode && sizeConstraints.Width == 0) ? double.PositiveInfinity : sizeConstraints.Width;
- sizeConstraints.Height = (InMeasureMode && sizeConstraints.Height == 0) ? double.PositiveInfinity : sizeConstraints.Height;
-
- if (child is Image)
+ if (InMeasureMode)
{
- // This is a hack to get FlexLayout to behave like it did in Forms
- // Forms always did its initial image measure unconstrained, which would return
- // the intrinsic size of the image (no scaling or aspect ratio adjustments)
+ var sizeConstraints = item.GetConstraints();
- sizeConstraints.Width = double.PositiveInfinity;
- sizeConstraints.Height = double.PositiveInfinity;
- }
+ sizeConstraints.Width = (InMeasureMode && sizeConstraints.Width == 0) ? double.PositiveInfinity : sizeConstraints.Width;
+ sizeConstraints.Height = (InMeasureMode && sizeConstraints.Height == 0) ? double.PositiveInfinity : sizeConstraints.Height;
+
+ if (child is Image)
+ {
+ // This is a hack to get FlexLayout to behave like it did in Forms
+ // Forms always did its initial image measure unconstrained, which would return
+ // the intrinsic size of the image (no scaling or aspect ratio adjustments)
+
+ sizeConstraints.Width = double.PositiveInfinity;
+ sizeConstraints.Height = double.PositiveInfinity;
+ }
- var request = child.Measure(sizeConstraints.Width, sizeConstraints.Height);
+ request = child.Measure(sizeConstraints.Width, sizeConstraints.Height);
+ }
+ else
+ {
+ // Arrange pass, do not ever run a measure here!
+ request = child.DesiredSize;
+ }
w = (float)request.Width;
h = (float)request.Height;
};
diff --git a/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs b/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
index 558ebb7b9222..8757a843e418 100644
--- a/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
+++ b/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
@@ -43,8 +43,7 @@ public void FlexLayoutMeasuresImagesUnconstrained()
root.Add(flexLayout);
flexLayout.Add(image as IView);
- var manager = new FlexLayoutManager(flexLayout);
- _ = manager.Measure(1000, 1000);
+ flexLayout.CrossPlatformMeasure(1000, 1000);
Assert.True(image.Passed, "Image should be measured unconstrained even if the FlexLayout is constrained.");
}
@@ -61,11 +60,9 @@ public void FlexLayoutRecognizesVisibilityChange()
flexLayout.Add(view as IView);
flexLayout.Add(view2 as IView);
- var manager = new FlexLayoutManager(flexLayout);
-
// Measure and arrange the layout while the first view is visible
- var measure = manager.Measure(1000, 1000);
- manager.ArrangeChildren(new Rect(Point.Zero, measure));
+ var measure = flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure));
// Keep track of where the second view is arranged
var whenVisible = view2.Frame.X;
@@ -73,9 +70,9 @@ public void FlexLayoutRecognizesVisibilityChange()
// Change the visibility
view.IsVisible = false;
- // Measure and arrange againg
- measure = manager.Measure(1000, 1000);
- manager.ArrangeChildren(new Rect(Point.Zero, measure));
+ // Measure and arrange again
+ measure = flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure));
var whenInvisible = view2.Frame.X;
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20696.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20696.cs
new file mode 100644
index 000000000000..1e3901cebe47
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20696.cs
@@ -0,0 +1,30 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue20696 : _IssuesUITest
+ {
+ public override string Issue => "[iOS] FlyoutHeader does not change its size after adding new content";
+
+ public Issue20696(TestDevice device) : base(device)
+ {
+ }
+
+ #if IOS
+ [Test]
+ #endif
+ public void FlyoutHeaderShouldBeResized()
+ {
+ _ = App.WaitForElement("GoToTest");
+ App.Tap("GoToTest");
+
+ _ = App.WaitForElement("button");
+ App.Tap("button");
+
+ //The test passes if the second button is visible in the flyout header
+ _ = App.WaitForElement("TestButton2");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9075.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9075.cs
new file mode 100644
index 000000000000..f38521234fe0
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9075.cs
@@ -0,0 +1,25 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue9075 : _IssuesUITest
+{
+ public override string Issue => "FlexLayout trigger Cycle GUI exception";
+
+ public Issue9075(TestDevice device)
+ : base(device)
+ { }
+
+ #if WINDOWS
+ [Test]
+ [Category(UITestCategories.Layout)]
+ #endif
+ public void FlexLayoutCycleException()
+ {
+ App.WaitForElement("Item2");
+
+ // Without exceptions, the test has passed.
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases/Issues/Issue9075.xaml b/src/Controls/tests/TestCases/Issues/Issue9075.xaml
new file mode 100644
index 000000000000..47956e52a11d
--- /dev/null
+++ b/src/Controls/tests/TestCases/Issues/Issue9075.xaml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases/Issues/Issue9075.xaml.cs b/src/Controls/tests/TestCases/Issues/Issue9075.xaml.cs
new file mode 100644
index 000000000000..8a3c555ff100
--- /dev/null
+++ b/src/Controls/tests/TestCases/Issues/Issue9075.xaml.cs
@@ -0,0 +1,60 @@
+using System.Collections.ObjectModel;
+using Microsoft.Maui.Controls.Internals;
+using Microsoft.Maui.Controls;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.None, 9075, "FlexLayout trigger Cycle GUI exception", PlatformAffected.UWP)]
+ public partial class Issue9075 : ContentPage
+ {
+ public Issue9075()
+ {
+ InitializeComponent();
+
+ BindingContext = new Issue9075ViewModel();
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ public partial class Issue9075ViewModel : BindableObject
+ {
+ List _carouselCards = new()
+ {
+ 49,
+ 12,
+ 50,
+ 10,
+ 25,
+ 9,
+ 99,
+ 77
+ };
+
+ ObservableCollection> _carouselGrids = new();
+
+ public ObservableCollection> CarouselGrids
+ {
+ get => _carouselGrids;
+ set
+ {
+ _carouselGrids = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public Issue9075ViewModel()
+ {
+ _InitGrids();
+ }
+
+ private void _InitGrids()
+ {
+ foreach (var card in _carouselCards)
+ {
+ _carouselGrids.Add(new ObservableCollection(Enumerable.Range(1, card).Select(x=> $"Item{x}")));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue20696.cs b/src/Controls/tests/UITests/Tests/Issues/Issue20696.cs
deleted file mode 100644
index 2c5cb341d287..000000000000
--- a/src/Controls/tests/UITests/Tests/Issues/Issue20696.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using NUnit.Framework;
-using UITest.Appium;
-using UITest.Core;
-
-namespace Microsoft.Maui.AppiumTests.Issues
-{
- public class Issue20696 : _IssuesUITest
- {
- public override string Issue => "[iOS] FlyoutHeader does not change its size after adding new content";
-
- public Issue20696(TestDevice device) : base(device)
- {
- }
-
- [Test]
- public void FlyoutHeaderShouldBeResized()
- {
- this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.Android, TestDevice.Mac, TestDevice.Windows });
-
- try
- {
- _ = App.WaitForElement("GoToTest");
- App.Click("GoToTest");
-
- _ = App.WaitForElement("button");
- App.Click("button");
-
- //The test passes if the second button is visible in the flyout header
- _ = App.WaitForElement("TestButton2");
- }
- finally
- {
- Reset();
- }
- }
- }
-}