diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_GridLayouting.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_GridLayouting.cs
index 55cfceb3ba52..572b953aaf25 100644
--- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_GridLayouting.cs
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_GridLayouting.cs
@@ -2133,13 +2133,7 @@ public void When_One_Fixed_Size_Child_With_Margin_Right_And_Stretch()
 #if !WINAPPSDK
 	private static Size GetUnclippedDesiredSize(UIElement element)
 	{
-#if UNO_REFERENCE_API
 		return element.m_unclippedDesiredSize;
-#else
-		var layouterElement = (ILayouterElement)element;
-		var layouter = (Layouter)layouterElement.Layouter;
-		return layouter._unclippedDesiredSize;
-#endif
 	}
 #endif
 
diff --git a/src/Uno.UI/Microsoft/UI/Xaml/Controls/PullToRefresh/Native/NativeRefreshControl.Android.cs b/src/Uno.UI/Microsoft/UI/Xaml/Controls/PullToRefresh/Native/NativeRefreshControl.Android.cs
index 2ec8ebc39ffa..01cadcb9a855 100644
--- a/src/Uno.UI/Microsoft/UI/Xaml/Controls/PullToRefresh/Native/NativeRefreshControl.Android.cs
+++ b/src/Uno.UI/Microsoft/UI/Xaml/Controls/PullToRefresh/Native/NativeRefreshControl.Android.cs
@@ -21,7 +21,7 @@
 
 namespace Uno.UI.Xaml.Controls;
 
-public partial class NativeRefreshControl : SwipeRefreshLayout, IShadowChildrenProvider, DependencyObject, ILayouterElement
+public partial class NativeRefreshControl : SwipeRefreshLayout, IShadowChildrenProvider, DependencyObject
 {
 	// Distance in pixels a touch can wander before we think the user is scrolling
 	// https://developer.android.com/reference/android/view/ViewConfiguration.html#getScaledTouchSlop()
@@ -36,7 +36,6 @@ public partial class NativeRefreshControl : SwipeRefreshLayout, IShadowChildrenP
 
 	public NativeRefreshControl() : base(ContextHelper.Current)
 	{
-		_layouter = new NativeRefreshControlLayouter(this);
 	}
 
 	internal Android.Views.View Content
@@ -162,93 +161,9 @@ public override bool OnTouchEvent(MotionEvent e)
 
 	List<View> IShadowChildrenProvider.ChildrenShadow => Content != null ? new List<View>(1) { Content as View } : _emptyList;
 
-	private ILayouter _layouter;
-
-	ILayouter ILayouterElement.Layouter => _layouter;
-	Size ILayouterElement.LastAvailableSize => LayoutInformation.GetAvailableSize(this);
-	bool ILayouterElement.IsMeasureDirty => true;
-	bool ILayouterElement.IsFirstMeasureDoneAndManagedElement => false;
-	bool ILayouterElement.StretchAffectsMeasure => true;
-	bool ILayouterElement.IsMeasureDirtyPathDisabled => true;
-
 	public override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
 	{
 		base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
-		((ILayouterElement)this).OnMeasureInternal(widthMeasureSpec, heightMeasureSpec);
-	}
-
-	void ILayouterElement.SetMeasuredDimensionInternal(int width, int height)
-	{
-		SetMeasuredDimension(width, height);
-	}
-
-	partial void OnLayoutPartial(bool changed, int left, int top, int right, int bottom)
-	{
-		var newSize = new Rect(0, 0, right - left, bottom - top).PhysicalToLogicalPixels();
-
-		// WARNING: The layouter must be called every time here,
-		// even if the size has not changed. Failing to call the layouter
-		// may leave the default ScrollViewer implementation place 
-		// the child at an invalid location when the visibility changes.
-
-		_layouter.Arrange(newSize);
-	}
-
-	private class NativeRefreshControlLayouter : Layouter
-	{
-		public NativeRefreshControlLayouter(NativeRefreshControl view) : base(view)
-		{
-		}
-
-		private NativeRefreshControl RefreshControl => Panel as NativeRefreshControl;
-
-		protected override void MeasureChild(View child, int widthSpec, int heightSpec)
-		{
-			var childMargin = (child as FrameworkElement)?.Margin ?? Thickness.Empty;
-
-			RefreshControl.Content?.Measure(widthSpec, heightSpec);
-		}
-
-		protected override Size MeasureOverride(Size availableSize)
-		{
-			var child = RefreshControl.Content;
-
-			var desiredChildSize = default(Size);
-			if (child != null)
-			{
-				var scrollSpace = availableSize;
-
-				desiredChildSize = MeasureChild(child, scrollSpace);
-
-				// Give opportunity to the the content to define the viewport size itself
-				(child as ICustomScrollInfo)?.ApplyViewport(ref desiredChildSize);
-			}
-
-			return desiredChildSize;
-		}
-
-		protected override Size ArrangeOverride(Size slotSize)
-		{
-			var child = RefreshControl.Content;
-
-			if (child != null)
-			{
-				ArrangeChild(child, new Rect(
-					0,
-					0,
-					slotSize.Width,
-					slotSize.Height
-				));
-
-				// Give opportunity to the the content to define the viewport size itself
-				(child as ICustomScrollInfo)?.ApplyViewport(ref slotSize);
-
-			}
-
-			return slotSize;
-		}
-
-		protected override string Name => Panel.Name;
 	}
 
 	private ViewGroup GetDescendantScrollable()
diff --git a/src/Uno.UI/UI/Xaml/Controls/FlipView/NativePagedView.Android.cs b/src/Uno.UI/UI/Xaml/Controls/FlipView/NativePagedView.Android.cs
index 59d4037c65ad..57bb8911f085 100644
--- a/src/Uno.UI/UI/Xaml/Controls/FlipView/NativePagedView.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/FlipView/NativePagedView.Android.cs
@@ -25,36 +25,21 @@ private void Initialize()
 		{
 			InitializeBinder();
 			LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent);
-			_layouter = new NativePagedViewLayouter(this);
 		}
 
-		private ILayouter _layouter;
-
-		ILayouter ILayouterElement.Layouter => _layouter;
-		Size ILayouterElement.LastAvailableSize => LayoutInformation.GetAvailableSize(this);
-		bool ILayouterElement.IsMeasureDirty => true;
-		bool ILayouterElement.IsFirstMeasureDoneAndManagedElement => false;
-		bool ILayouterElement.StretchAffectsMeasure => false;
-		bool ILayouterElement.IsMeasureDirtyPathDisabled => true;
-
 		protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
 		{
-			var measuredSize = ((ILayouterElement)this).OnMeasureInternal(widthMeasureSpec, heightMeasureSpec);
-
-			var logicalMeasuredSize = measuredSize.PhysicalToLogicalPixels();
-
-			//We call ViewPager.OnMeasure here, because it creates the page views.
-			base.OnMeasure(
-				ViewHelper.SpecFromLogicalSize(logicalMeasuredSize.Width),
-				ViewHelper.SpecFromLogicalSize(logicalMeasuredSize.Height)
-			);
+			base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
+		}
 
-			IFrameworkElementHelper.OnMeasureOverride(this);
+		Size ILayouterElement.Measure(Size availableSize)
+		{
+			return default; // TODO
 		}
 
-		void ILayouterElement.SetMeasuredDimensionInternal(int width, int height)
+		void ILayouterElement.Arrange(Rect finalRect)
 		{
-			SetMeasuredDimension(width, height);
+			// TODO
 		}
 
 		//TODO generated code
@@ -73,7 +58,7 @@ partial void OnLayoutPartial(bool changed, int left, int top, int right, int bot
 			{
 				_lastLayoutSize = newSize;
 
-				_layouter.Arrange(new global::Windows.Foundation.Rect(0, 0, newSize.Width, newSize.Height));
+				//_layouter.Arrange(new global::Windows.Foundation.Rect(0, 0, newSize.Width, newSize.Height));
 			}
 		}
 
@@ -99,48 +84,48 @@ public override void RemoveViewAt(int index)
 
 		public bool IsHeightConstrained(View requester) => (Parent as ILayoutConstraints)?.IsHeightConstrained(this) ?? false;
 
-		private class NativePagedViewLayouter : Layouter
-		{
-
-			public NativePagedViewLayouter(NativePagedView view) : base(view) { }
-
-			protected override string Name => Panel.Name;
-
-
-			protected override void MeasureChild(View view, int widthSpec, int heightSpec)
-			{
-				(Panel as NativePagedView).MeasureChild(view, widthSpec, heightSpec);
-			}
-
-			protected override Size ArrangeOverride(Size finalSize)
-			{
-				//Do nothing here. FrameworkElementMixins.OnLayout calls ViewPager.OnLayout, which lays out its visible page.
-				return finalSize;
-			}
-
-			protected override Size MeasureOverride(Size availableSize)
-			{
-				var sizeThatFits = IFrameworkElementHelper.SizeThatFits(Panel, availableSize);
-
-				double maxChildWidth = 0f, maxChildHeight = 0f;
-
-				//Per the link, if the NativePagedView has no fixed size in a dimension, wrap it to the size of its largest child. - http://stackoverflow.com/questions/8394681/android-i-am-unable-to-have-viewpager-wrap-content
-				//This might be brittle if items have varying dimensions along the unfixed axis; one (hackish) solution would be to increase OffscreenPageLimit (the number of offscreen pages that are kept).
-				if (double.IsPositiveInfinity(sizeThatFits.Width) || double.IsPositiveInfinity(sizeThatFits.Height))
-				{
-					foreach (var child in this.GetChildren())
-					{
-						var desiredChildSize = MeasureChild(child, sizeThatFits);
-						maxChildWidth = Math.Max(maxChildWidth, desiredChildSize.Width);
-						maxChildHeight = Math.Max(maxChildHeight, desiredChildSize.Height);
-					}
-				}
-
-				return new Size(
-					!double.IsPositiveInfinity(sizeThatFits.Width) ? sizeThatFits.Width : maxChildWidth,
-					!double.IsPositiveInfinity(sizeThatFits.Height) ? sizeThatFits.Height : maxChildHeight
-				);
-			}
-		}
+		//private class NativePagedViewLayouter : Layouter
+		//{
+
+		//	public NativePagedViewLayouter(NativePagedView view) : base(view) { }
+
+		//	protected override string Name => Panel.Name;
+
+
+		//	protected override void MeasureChild(View view, int widthSpec, int heightSpec)
+		//	{
+		//		(Panel as NativePagedView).MeasureChild(view, widthSpec, heightSpec);
+		//	}
+
+		//	protected override Size ArrangeOverride(Size finalSize)
+		//	{
+		//		//Do nothing here. FrameworkElementMixins.OnLayout calls ViewPager.OnLayout, which lays out its visible page.
+		//		return finalSize;
+		//	}
+
+		//	protected override Size MeasureOverride(Size availableSize)
+		//	{
+		//		var sizeThatFits = IFrameworkElementHelper.SizeThatFits(Panel, availableSize);
+
+		//		double maxChildWidth = 0f, maxChildHeight = 0f;
+
+		//		//Per the link, if the NativePagedView has no fixed size in a dimension, wrap it to the size of its largest child. - http://stackoverflow.com/questions/8394681/android-i-am-unable-to-have-viewpager-wrap-content
+		//		//This might be brittle if items have varying dimensions along the unfixed axis; one (hackish) solution would be to increase OffscreenPageLimit (the number of offscreen pages that are kept).
+		//		if (double.IsPositiveInfinity(sizeThatFits.Width) || double.IsPositiveInfinity(sizeThatFits.Height))
+		//		{
+		//			foreach (var child in this.GetChildren())
+		//			{
+		//				var desiredChildSize = MeasureChild(child, sizeThatFits);
+		//				maxChildWidth = Math.Max(maxChildWidth, desiredChildSize.Width);
+		//				maxChildHeight = Math.Max(maxChildHeight, desiredChildSize.Height);
+		//			}
+		//		}
+
+		//		return new Size(
+		//			!double.IsPositiveInfinity(sizeThatFits.Width) ? sizeThatFits.Width : maxChildWidth,
+		//			!double.IsPositiveInfinity(sizeThatFits.Height) ? sizeThatFits.Height : maxChildHeight
+		//		);
+		//	}
+		//}
 	}
 }
diff --git a/src/Uno.UI/UI/Xaml/Controls/ItemsStackPanel/ManagedItemsStackPanel.cs b/src/Uno.UI/UI/Xaml/Controls/ItemsStackPanel/ManagedItemsStackPanel.cs
index 95977e44dd50..f08b2d69ac9e 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ItemsStackPanel/ManagedItemsStackPanel.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ItemsStackPanel/ManagedItemsStackPanel.cs
@@ -17,10 +17,6 @@ public partial class ManagedItemsStackPanel : Panel
 	{
 		ManagedVirtualizingPanelLayout _layout;
 
-#if !__IOS__
-		internal bool ShouldInterceptInvalidate { get; set; }
-#endif
-
 		public ManagedItemsStackPanel()
 		{
 			if (FeatureConfiguration.ListViewBase.DefaultCacheLength.HasValue)
diff --git a/src/Uno.UI/UI/Xaml/Controls/ItemsWrapGrid/ItemsWrapGridLayout.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ItemsWrapGrid/ItemsWrapGridLayout.Android.cs
index acc1017400c6..e0bddc9456d9 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ItemsWrapGrid/ItemsWrapGridLayout.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ItemsWrapGrid/ItemsWrapGridLayout.Android.cs
@@ -79,7 +79,8 @@ bool isNewGroup
 				AddView(view, direction);
 
 				var slotSize = new Size(availableWidth, availableHeight).PhysicalToLogicalPixels();
-				var measuredSize = _layouter.MeasureChild(view, slotSize);
+				//var measuredSize = _layouter.MeasureChild(view, slotSize);
+				var measuredSize = slotSize; // TODO
 				var physicalMeasuredSize = measuredSize.LogicalToPhysicalPixels();
 				var measuredWidth = (int)physicalMeasuredSize.Width;
 				var measuredHeight = (int)physicalMeasuredSize.Height;
diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/ILayouter.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/ILayouter.cs
deleted file mode 100644
index 3174996ea4de..000000000000
--- a/src/Uno.UI/UI/Xaml/Controls/Layouter/ILayouter.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Windows.Foundation;
-
-#if __ANDROID__
-using View = Android.Views.View;
-using Font = Android.Graphics.Typeface;
-#elif __IOS__
-using View = UIKit.UIView;
-using Color = UIKit.UIColor;
-using Font = UIKit.UIFont;
-#elif __MACOS__
-using View = AppKit.NSView;
-using Color = AppKit.NSColor;
-using Font = AppKit.NSFont;
-#else
-using View = Microsoft.UI.Xaml.UIElement;
-#endif
-
-namespace Microsoft.UI.Xaml.Controls
-{
-	internal interface ILayouter
-	{
-		/// <summary>
-		/// Measures the current layout
-		/// </summary>
-		/// <param name="availableSize">The available size in virtual pixels</param>
-		/// <returns>The measured size in virtual pixels</returns>
-		Size Measure(Size availableSize);
-
-		/// <summary>
-		/// Arranges the current layout.
-		/// </summary>
-		/// <param name="finalSize">The maximum size to use when arranging the layout</param>
-		void Arrange(Rect finalRect);
-
-		/// <summary>
-		/// Measures the specified child.
-		/// </summary>
-		/// <param name="view">The view to measure</param>
-		/// <param name="slotSize">The maximum size the child can use.</param>
-		/// <returns>The size the view requires.</returns>
-		/// <remarks>
-		/// Provides the ability for external implementations to measure children.
-		/// Mainly used for compatibility with existing WPF/WinRT implementations.
-		/// </remarks>
-		Size MeasureChild(View view, Size slotSize);
-
-		/// <summary>
-		/// Arranges the specified view.
-		/// </summary>
-		/// <param name="view">The view to arrange</param>
-		/// <param name="frame">The frame available for the child.</param>
-		/// <remarks>
-		/// Provides the ability for external implementations to measure children.
-		/// Mainly used for compatibility with existing WPF/WinRT implementations.
-		/// </remarks>
-		void ArrangeChild(View view, Rect frame);
-
-		/// <summary>
-		/// Provides the desired size of the element, from the last measure phase.
-		/// </summary>
-		/// <param name="element">The element to get the measured with</param>
-		/// <returns>The measured size</returns>
-		Size GetDesiredSize(View view);
-	}
-}
diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.Android.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.Android.cs
deleted file mode 100644
index 9a13ccc5b082..000000000000
--- a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.Android.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Uno.Extensions;
-using Uno;
-using Uno.Foundation.Logging;
-using Uno.Collections;
-using Windows.Foundation;
-
-using View = Android.Views.View;
-using Font = Android.Graphics.Typeface;
-using System.Linq.Expressions;
-using Uno.UI;
-
-namespace Microsoft.UI.Xaml.Controls
-{
-	abstract partial class Layouter
-	{
-		public static void SetMeasuredDimensions(View view, int width, int height)
-		{
-			LayouterHelper.SetMeasuredDimensions(view, new object[] { width, height });
-		}
-
-		protected Size MeasureChildOverride(View view, Size slotSize)
-		{
-			var widthSpec = ViewHelper.SpecFromLogicalSize(slotSize.Width);
-			var heightSpec = ViewHelper.SpecFromLogicalSize(slotSize.Height);
-
-			var needsForceLayout =
-				(double.IsPositiveInfinity(slotSize.Width) || double.IsPositiveInfinity(slotSize.Height)) ||
-				// uno12315: ensure the native measure cache is not used when measure-spec has changed since.
-				FeatureConfiguration.FrameworkElement.InvalidateNativeCacheOnRemeasure && (
-					view.MeasuredWidth != ViewHelper.LogicalToPhysicalPixels(slotSize.Width) ||
-					view.MeasuredHeight != ViewHelper.LogicalToPhysicalPixels(slotSize.Height)
-				);
-
-			Uno.UI.Controls.BindableView.TryFastRequestLayout(view, needsForceLayout);
-
-			MeasureChild(view, widthSpec, heightSpec);
-
-			var ret = Uno.UI.Controls.BindableView.GetNativeMeasuredDimensionsFast(view)
-				.PhysicalToLogicalPixels();
-
-			return ret.AtMost(slotSize);
-		}
-
-		protected abstract void MeasureChild(View view, int widthSpec, int heightSpec);
-
-		protected void ArrangeChildOverride(View view, Rect frame)
-		{
-			LogArrange(view, frame);
-
-			var elt = view as UIElement;
-			var physicalFrame = frame.LogicalToPhysicalPixels();
-
-			try
-			{
-				elt?.SetFramePriorArrange(frame, physicalFrame);
-
-				view.Layout(
-					(int)physicalFrame.Left,
-					(int)physicalFrame.Top,
-					(int)physicalFrame.Right,
-					(int)physicalFrame.Bottom
-				);
-			}
-			finally
-			{
-				elt?.ResetFramePostArrange();
-			}
-		}
-	}
-
-	internal static partial class LayouterExtensions
-	{
-		public static IEnumerable<View> GetChildren(this Layouter layouter)
-		{
-			return (layouter.Panel as Android.Views.ViewGroup).GetChildren();
-		}
-	}
-}
diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.cs
deleted file mode 100644
index 7618142a586b..000000000000
--- a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.cs
+++ /dev/null
@@ -1,862 +0,0 @@
-// #define LOG_LAYOUT
-
-#if !UNO_REFERENCE_API
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Windows.Foundation;
-using Microsoft.UI.Xaml.Controls.Primitives;
-using Microsoft.UI.Xaml.Media;
-using Uno;
-using Uno.Extensions;
-using Uno.Foundation.Logging;
-using Uno.Collections;
-using Uno.Diagnostics.Eventing;
-using Uno.UI;
-using static System.Double;
-using static System.Math;
-using static Uno.UI.LayoutHelper;
-
-#if __ANDROID__
-using Android.Views;
-using View = Android.Views.View;
-using Font = Android.Graphics.Typeface;
-#elif __IOS__
-using View = UIKit.UIView;
-using Color = UIKit.UIColor;
-using Font = UIKit.UIFont;
-using CoreGraphics;
-#elif __MACOS__
-using View = AppKit.NSView;
-using Color = AppKit.NSColor;
-using Font = AppKit.NSFont;
-using CoreGraphics;
-#elif IS_UNIT_TESTS || __WASM__
-using View = Microsoft.UI.Xaml.UIElement;
-#endif
-
-namespace Microsoft.UI.Xaml.Controls
-{
-	internal abstract partial class Layouter : ILayouter
-	{
-		private static readonly IEventProvider _trace = Tracing.Get(FrameworkElement.TraceProvider.Id);
-		private readonly Logger _logDebug;
-
-		private readonly Size MaxSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
-
-		internal Size _unclippedDesiredSize;
-
-		private readonly UIElement _elementAsUIElement;
-
-		public IFrameworkElement Panel { get; }
-
-		protected Layouter(IFrameworkElement element)
-		{
-			Panel = element;
-			_elementAsUIElement = element as UIElement;
-
-			var log = this.Log();
-			if (log.IsEnabled(LogLevel.Debug))
-			{
-				_logDebug = log;
-			}
-		}
-
-		/// <summary>
-		/// Determine the size of the panel.
-		/// </summary>
-		/// <param name="availableSize">The available size, in logical pixels.</param>
-		/// <returns>The size of the panel, in logical pixel.</returns>
-		public Size Measure(Size availableSize)
-		{
-			using var traceActivity = _trace.IsEnabled
-				? _trace.WriteEventActivity(
-					FrameworkElement.TraceProvider.FrameworkElement_MeasureStart,
-					FrameworkElement.TraceProvider.FrameworkElement_MeasureStop,
-					new object[] { LoggingOwnerTypeName, Panel.GetDependencyObjectId() }
-				)
-				: null;
-
-			if (Panel.Visibility == Visibility.Collapsed)
-			{
-				// A collapsed element should not be measured at all
-				return default;
-			}
-
-			try
-			{
-				if (_elementAsUIElement?.IsVisualTreeRoot ?? false)
-				{
-					UIElement.IsLayoutingVisualTreeRoot = true;
-				}
-
-				var (minSize, maxSize) = Panel.GetMinMax();
-				var marginSize = Panel.GetMarginSize();
-
-				// NaN values are accepted as input here, particularly when coming from
-				// SizeThatFits in Image or Scrollviewer. Clamp the value here as it is reused
-				// below for the clipping value.
-				var frameworkAvailableSize = availableSize
-					.NumberOrDefault(MaxSize);
-
-				frameworkAvailableSize = frameworkAvailableSize
-					.Subtract(marginSize)
-					.AtLeastZero()
-					.AtMost(maxSize);
-
-				// TODO: This commented code was done as part of aligning layouting on mobile platforms.
-				// We are reverting those changes as they require more changes, but keeping them
-				// commented for future reference.
-				//if (Panel is not ILayoutOptOut { ShouldUseMinSize: false })
-				//{
-				//	frameworkAvailableSize = frameworkAvailableSize.AtLeast(minSize);
-				//}
-
-				var desiredSize = MeasureOverride(frameworkAvailableSize);
-				LayoutInformation.SetAvailableSize(Panel, availableSize);
-
-				_logDebug?.Trace($"{this}.MeasureOverride(availableSize={availableSize}); frameworkAvailableSize={frameworkAvailableSize}; desiredSize={desiredSize}");
-
-				if (
-					double.IsNaN(desiredSize.Width)
-					|| double.IsNaN(desiredSize.Height)
-					|| double.IsInfinity(desiredSize.Width)
-					|| double.IsInfinity(desiredSize.Height)
-				)
-				{
-					throw new InvalidOperationException($"{this}: Invalid measured size {desiredSize}. NaN or Infinity are invalid desired size.");
-				}
-
-				desiredSize = desiredSize
-					.AtLeast((Panel as ILayoutOptOut)?.ShouldUseMinSize == false ? Size.Empty : minSize)
-					.AtLeastZero();
-
-				if (_elementAsUIElement is not null)
-				{
-					_elementAsUIElement.EnsureLayoutStorage();
-				}
-
-				_unclippedDesiredSize = desiredSize;
-
-				var clippedDesiredSize = desiredSize
-					// TODO: This commented code was done as part of aligning layouting on mobile platforms.
-					// We are reverting those changes as they require more changes, but keeping them
-					// commented for future reference.
-					//.AtMost(maxSize)
-					.AtMost(frameworkAvailableSize) // TODO: This line shouldn't be there (ie, frameworkAvailableSize should be maxSize).
-					.Add(marginSize)
-					// Making sure after adding margins that clipped DesiredSize is not bigger than the AvailableSize
-					// TODO: This commented code was done as part of aligning layouting on mobile platforms.
-					// We are reverting those changes as they require more changes, but keeping them
-					// commented for future reference.
-					//.AtMost(availableSize)
-					// Margin may be negative
-					.AtLeastZero();
-
-				// DesiredSize must include margins
-				// TODO: on UWP, it's not clipped. See test When_MinWidth_SmallerThan_AvailableSize
-				LayoutInformation.SetDesiredSize(Panel, clippedDesiredSize);
-
-				// We return "clipped" desiredSize to caller... the unclipped version stays internal
-				return clippedDesiredSize;
-			}
-			finally
-			{
-				if (_elementAsUIElement?.IsVisualTreeRoot ?? false)
-				{
-					UIElement.IsLayoutingVisualTreeRoot = false;
-				}
-			}
-		}
-
-		private static bool IsCloseReal(double a, double b)
-		{
-			var x = Math.Abs((a - b) / (b == 0d ? 1d : b));
-			return x < 1.85e-3d;
-		}
-
-		private static bool IsLessThanAndNotCloseTo(double a, double b)
-		{
-			return (a < b) && !IsCloseReal(a, b);
-		}
-
-		/// <summary>
-		/// Places the children of the panel using a specific size, in logical pixels.
-		/// </summary>
-		public void Arrange(Rect finalRect)
-		{
-			using var traceActivity = _trace.IsEnabled
-				? _trace.WriteEventActivity(
-					FrameworkElement.TraceProvider.FrameworkElement_ArrangeStart,
-					FrameworkElement.TraceProvider.FrameworkElement_ArrangeStop,
-					new object[] { LoggingOwnerTypeName, Panel.GetDependencyObjectId() }
-				)
-				: null;
-
-			try
-			{
-				if (_elementAsUIElement?.IsVisualTreeRoot ?? false)
-				{
-					UIElement.IsLayoutingVisualTreeRoot = true;
-				}
-
-				if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-				{
-					this.Log().DebugFormat("[{0}/{1}] Arrange({2}/{3}/{4}/{5})", LoggingOwnerTypeName, Name, GetType(), Panel.Name, finalRect, Panel.Margin);
-				}
-
-				if (_elementAsUIElement is not null)
-				{
-					_elementAsUIElement.EnsureLayoutStorage();
-				}
-
-				var clippedArrangeSize = _elementAsUIElement?.ClippedFrame is Rect clip && !_elementAsUIElement.IsArrangeDirty
-					? clip.Size
-					: finalRect.Size;
-
-				bool allowClipToSlot;
-				bool needsClipToSlot;
-
-#if !IS_UNIT_TESTS
-				if (Panel is ICustomClippingElement customClippingElement)
-				{
-					// Some controls may control itself how clipping is applied
-					allowClipToSlot = customClippingElement.AllowClippingToLayoutSlot;
-					needsClipToSlot = customClippingElement.ForceClippingToLayoutSlot;
-				}
-				else
-#endif
-				{
-					allowClipToSlot = true;
-					needsClipToSlot = false;
-				}
-
-				_logDebug?.Debug($"{this}: InnerArrangeCore({finalRect}) - allowClip={allowClipToSlot}, clippedArrangeSize={clippedArrangeSize}, _unclippedDesiredSize={_unclippedDesiredSize}, forcedClipping={needsClipToSlot}");
-
-				var arrangeSize = finalRect
-					.Size
-					.AtLeastZero(); // 0.0,0.0
-
-				if (allowClipToSlot && !needsClipToSlot)
-				{
-					if (IsLessThanAndNotCloseTo(clippedArrangeSize.Width, _unclippedDesiredSize.Width))
-					{
-						_logDebug?.Debug($"{this}: (arrangeSize.Width) {clippedArrangeSize.Width} < {_unclippedDesiredSize.Width}: NEEDS CLIPPING.");
-						needsClipToSlot = true;
-						// TODO: This commented code was done as part of aligning layouting on mobile platforms.
-						// We are reverting those changes as they require more changes, but keeping them
-						// commented for future reference.
-						//arrangeSize.Width = _unclippedDesiredSize.Width;
-					}
-
-					if (IsLessThanAndNotCloseTo(clippedArrangeSize.Height, _unclippedDesiredSize.Height))
-					{
-						_logDebug?.Debug($"{this}: (arrangeSize.Height) {clippedArrangeSize.Height} < {_unclippedDesiredSize.Height}: NEEDS CLIPPING.");
-						needsClipToSlot = true;
-						// TODO: This commented code was done as part of aligning layouting on mobile platforms.
-						// We are reverting those changes as they require more changes, but keeping them
-						// commented for future reference.
-						//arrangeSize.Height = _unclippedDesiredSize.Height;
-					}
-				}
-
-				// Alignment==Stretch --> arrange at the slot size minus margins
-				// Alignment!=Stretch --> arrange at the unclippedDesiredSize
-				if (Panel is not Microsoft.UI.Xaml.Shapes.Shape and not ContentControl)
-				{
-					// Uno specific: Shapes arrange is relying on "wrong" layouter logic to be arranged properly
-					// The "Panel is not Shape" check should be removed when we're removing the legacy shape measure/arrange
-					// Also, it seems ContentControl is causing issues (probably related to content presenter bypass?)
-					if (Panel.HorizontalAlignment != HorizontalAlignment.Stretch)
-					{
-						arrangeSize.Width = _unclippedDesiredSize.Width;
-					}
-					if (Panel.VerticalAlignment != VerticalAlignment.Stretch)
-					{
-						arrangeSize.Height = _unclippedDesiredSize.Height;
-					}
-				}
-
-				var (_, maxSize) = this.Panel.GetMinMax();
-				//var marginSize = this.Panel.GetMarginSize();
-
-				// We have to choose max between _unclippedDesiredSize and maxSize here, because
-				// otherwise setting of max property could cause arrange at less then _unclippedDesiredSize.
-				// Clipping by Max is needed to limit stretch here
-				var effectiveMaxSize = Max(_unclippedDesiredSize, maxSize);
-
-				if (allowClipToSlot)
-				{
-					if (IsLessThanAndNotCloseTo(effectiveMaxSize.Width, arrangeSize.Width))
-					{
-						_logDebug?.Debug($"{this}: (effectiveMaxSize.Width) {effectiveMaxSize.Width} < {arrangeSize.Width}: NEEDS CLIPPING.");
-						needsClipToSlot = true;
-						arrangeSize.Width = effectiveMaxSize.Width;
-					}
-
-					if (IsLessThanAndNotCloseTo(effectiveMaxSize.Height, arrangeSize.Height))
-					{
-						_logDebug?.Debug($"{this}: (effectiveMaxSize.Height) {effectiveMaxSize.Height} < {arrangeSize.Height}: NEEDS CLIPPING.");
-						needsClipToSlot = true;
-						arrangeSize.Height = effectiveMaxSize.Height;
-					}
-				}
-
-				var innerInkSize = ArrangeOverride(arrangeSize);
-				var clippedInkSize = innerInkSize.AtMost(maxSize);
-
-				// TODO: This commented code was done as part of aligning layouting on mobile platforms.
-				// We are reverting those changes as they require more changes, but keeping them
-				// commented for future reference.
-				//if (IsLessThanAndNotCloseTo(clippedInkSize.Width, innerInkSize.Width) || IsLessThanAndNotCloseTo(clippedInkSize.Height, innerInkSize.Height))
-				//{
-				//	needsClipToSlot = true;
-				//}
-
-				//var clientSize = finalRect.Size
-				//	.Subtract(marginSize)
-				//	.AtLeastZero();
-
-				//var (offset, overflow) = Panel.GetAlignmentOffset(clientSize, clippedInkSize);
-				//var margin = Panel.Margin;
-
-				//offset = new Point(
-				//	offset.X + finalRect.X + margin.Left,
-				//	offset.Y + finalRect.Y + margin.Top
-				//);
-
-				//if (overflow)
-				//{
-				//	needsClipToSlot = true;
-				//}
-
-
-				if (_elementAsUIElement != null)
-				{
-					//_elementAsUIElement.LayoutSlotWithMarginsAndAlignments = new Rect(offset, innerInkSize);
-					//var layoutFrame = new Rect(offset, clippedInkSize);
-
-					// Calculate clipped frame.
-					//var clippedFrameWithParentOrigin = layoutFrame.IntersectWith(finalRect.DeflateBy(margin)) ?? Rect.Empty;
-
-					// Rebase the origin of the clipped frame to layout
-					//_elementAsUIElement.ClippedFrame = new Rect(
-					//	clippedFrameWithParentOrigin.X - layoutFrame.X,
-					//	clippedFrameWithParentOrigin.Y - layoutFrame.Y,
-					//	clippedFrameWithParentOrigin.Width,
-					//	clippedFrameWithParentOrigin.Height);
-
-					_elementAsUIElement.RenderSize = clippedInkSize; // TODO: This should be innerInkSize
-					_elementAsUIElement.NeedsClipToSlot = needsClipToSlot;
-					_elementAsUIElement.ApplyClip();
-
-					if (Panel is FrameworkElement fe)
-					{
-						fe.OnLayoutUpdated();
-					}
-				}
-				else if (Panel is IFrameworkElement_EffectiveViewport evp)
-				{
-					evp.OnLayoutUpdated();
-				}
-			}
-			finally
-			{
-				if (_elementAsUIElement?.IsVisualTreeRoot ?? false)
-				{
-					UIElement.IsLayoutingVisualTreeRoot = false;
-				}
-			}
-		}
-
-		/// <summary>
-		/// Determine the size of the panel.
-		/// </summary>
-		/// <param name="availableSize">The available size, in logical pixels.</param>
-		/// <returns>The size of the panel, in logical pixel.</returns>
-		protected abstract Size MeasureOverride(Size availableSize);
-
-		/// <summary>
-		/// Places the children of the panel using a specific size, in logical pixels.
-		/// </summary>
-		/// <param name="finalSize">The final panel size</param>
-		protected abstract Size ArrangeOverride(Size finalSize);
-
-		/// <summary>
-		/// Provides the desired size of the element, from the last measure phase.
-		/// </summary>
-		/// <param name="view">The element to get the measured with</param>
-		/// <returns>The measured size</returns>
-		Size ILayouter.GetDesiredSize(View view)
-			=> LayoutInformation.GetDesiredSize(view);
-
-		protected Size MeasureChild(View view, Size slotSize)
-		{
-			var frameworkElement = view as IFrameworkElementInternal;
-			var ret = default(Size);
-
-			// NaN values are accepted as input for MeasureOverride, but are treated as Infinity.
-			slotSize = slotSize.NumberOrDefault(MaxSize);
-
-			if (frameworkElement?.Visibility == Visibility.Collapsed)
-			{
-				// By default iOS views measure to normal size, even if they're hidden.
-				// We want the collapsed behavior, so we return a 0,0 size instead.
-
-				// Note: Visibility is checked in both Measure and MeasureChild, since some IFrameworkElement children may not have their own Layouter
-				LayoutInformation.SetDesiredSize(view, ret);
-
-				if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-				{
-					var viewName = frameworkElement.SelectOrDefault(f => f.Name, "NativeView");
-
-					this.Log().DebugFormat(
-						"[{0}/{1}] MeasureChild(HIDDEN/{2}/{3}/{4}/{5}) = {6}",
-						LoggingOwnerTypeName,
-						Name,
-						view.GetType(),
-						viewName,
-						slotSize,
-						frameworkElement.Margin,
-						ret
-					);
-				}
-
-				return ret;
-			}
-
-			if (frameworkElement != null
-				&& !(frameworkElement is FrameworkElement)
-				&& !(frameworkElement is Image)
-				)
-			{
-				// For IFrameworkElement implementers that are not FrameworkElements, the constraint logic must
-				// be performed by the parent. Otherwise, the native element will take the size it needs without
-				// regards to explicit XAML size characteristics. The Android ProgressBar is a good example of
-				// that behavior.
-
-				var margin = frameworkElement.Margin;
-
-				if (margin != Thickness.Empty)
-				{
-					// Apply the margin for framework elements, as if it were padding to the child.
-					slotSize = new Size(
-						Math.Max(0, slotSize.Width - margin.Left - margin.Right),
-						Math.Max(0, slotSize.Height - margin.Top - margin.Bottom)
-					);
-				}
-
-				// Alias the Dependency Properties values to avoid double calls.
-				var childWidth = frameworkElement.Width;
-				var childMaxWidth = frameworkElement.MaxWidth;
-				var childHeight = frameworkElement.Height;
-				var childMaxHeight = frameworkElement.MaxHeight;
-
-				var optionalMaxWidth = !IsInfinity(childMaxWidth) && !IsNaN(childMaxWidth) ? childMaxWidth : (double?)null;
-				var optionalWidth = !IsNaN(childWidth) ? childWidth : (double?)null;
-				var optionalMaxHeight = !IsInfinity(childMaxHeight) && !IsNaN(childMaxHeight) ? childMaxHeight : (double?)null;
-				var optionalHeight = !IsNaN(childHeight) ? childHeight : (double?)null;
-
-				// After the margin has been removed, ensure the remaining space slot does not go
-				// over the explicit or maximum size of the child.
-				if (optionalMaxWidth != null || optionalWidth != null)
-				{
-					var constrainedWidth = Math.Min(
-						optionalMaxWidth ?? double.PositiveInfinity,
-						optionalWidth ?? double.PositiveInfinity
-					);
-
-					slotSize.Width = Math.Min(slotSize.Width, constrainedWidth);
-				}
-
-				if (optionalMaxHeight != null || optionalHeight != null)
-				{
-					var constrainedHeight = Math.Min(
-						optionalMaxHeight ?? double.PositiveInfinity,
-						optionalHeight ?? double.PositiveInfinity
-					);
-
-					slotSize.Height = Math.Min(slotSize.Height, constrainedHeight);
-				}
-			}
-
-			ret = MeasureChildOverride(view, slotSize);
-
-			if (
-				IsPositiveInfinity(ret.Height) || IsPositiveInfinity(ret.Width)
-			)
-			{
-				if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Error))
-				{
-					var viewName = frameworkElement.SelectOrDefault(f => f.Name, "NativeView");
-					var margin = frameworkElement.SelectOrDefault(f => f.Margin, Thickness.Empty);
-
-					this.Log().ErrorFormat(
-						"[{0}/{1}] MeasureChild({2}/{3}/{4}/{5}) = Child returned INFINITY {6}",
-						LoggingOwnerTypeName,
-						Name,
-						view.GetType(),
-						viewName,
-						slotSize,
-						margin,
-						ret
-					);
-				}
-
-				ret = new Size(0, 0);
-			}
-			else
-			{
-				if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-				{
-					var viewName = frameworkElement.SelectOrDefault(f => f.Name, "NativeView");
-					var margin = frameworkElement.SelectOrDefault(f => f.Margin, Thickness.Empty);
-
-					this.Log().DebugFormat(
-						"[{0}/{1}] MeasureChild({2}/{3}/{4}/{5}) = {6}",
-						LoggingOwnerTypeName,
-						Name,
-						view.GetType(),
-						viewName,
-						slotSize,
-						margin,
-						ret
-					);
-				}
-			}
-
-			var hasLayouter = frameworkElement?.HasLayouter ?? false;
-			if (!hasLayouter || frameworkElement.Visibility == Visibility.Collapsed)
-			{
-				// For native controls only - because it's already set in Layouter.Measure()
-				// for Uno's managed controls
-				LayoutInformation.SetDesiredSize(view, ret);
-			}
-
-
-			return ret;
-		}
-
-		/// <summary>
-		/// Arranges the location a child in the current panel
-		/// </summary>
-		/// <param name="view">The child instance</param>
-		/// <param name="frame">The rectangle to use, in Logical position</param>
-		public void ArrangeChild(View view, Rect frame)
-		{
-			if ((view as IFrameworkElement)?.Visibility == Visibility.Collapsed)
-			{
-				return;
-			}
-
-			LayoutInformation.SetLayoutSlot(view, frame);
-
-			// Note: This is not matching Windows.
-			// Applying alignments should depend on what ArrangeOverride returns (as in Skia and Wasm).
-			var (finalFrame, clippedFrame) = ApplyMarginAndAlignments(view, frame);
-			if (view is UIElement elt)
-			{
-				elt.LayoutSlotWithMarginsAndAlignments = finalFrame;
-				elt.ClippedFrame = clippedFrame;
-			}
-
-
-			ArrangeChildOverride(view, finalFrame);
-		}
-
-#if __ANDROID__ || __IOS__ || __MACOS__
-		private void LogArrange(View view, Rect frame)
-		{
-			if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-			{
-				var viewName = (view as IFrameworkElement).SelectOrDefault(f => f.Name, "NativeView");
-				var margin = (view as IFrameworkElement).SelectOrDefault(f => f.Margin, Thickness.Empty);
-
-				this.Log().DebugFormat("[{0}/{1}] ArrangeChild({2}/{3}/{4}/{5})", LoggingOwnerTypeName, Name, view.GetType(), viewName, frame, margin);
-			}
-
-		}
-#endif
-
-		protected abstract string Name { get; }
-
-		private (Rect layoutFrame, Rect clippedFrame) ApplyMarginAndAlignments(View view, Rect frame)
-		{
-			// In this implementation, since we do not have the ability to intercept properly the measure and arrange
-			// because of the type of hierarchy (inheriting from native views), we must apply the margins and alignments
-			// from within the panel to its children. This makes the authoring of custom panels that do not inherit from
-			// Panel that do not use this helper a bit more complex, but for all other panels that use this
-			// layouter, the logic is implied.
-
-			// The result "layoutFrame" gives the positioning of the element, relative to the origin of the parent's frame.
-			// The result "clippedFrame" gives the resulting boundaries of the element.
-			// If clipping is required, that's were it should occurs.
-
-			if (view is IFrameworkElement frameworkElement)
-			{
-				// Apply the margin for framework elements, as if it were padding to the child.
-
-				var (x, y, width, height) = frame;
-
-				// capture the child's state to avoid getting DependencyProperties values multiple times.
-				var childVerticalAlignment = frameworkElement.VerticalAlignment;
-				var childHorizontalAlignment = frameworkElement.HorizontalAlignment;
-
-				AdjustAlignment(view, ref childHorizontalAlignment, ref childVerticalAlignment);
-
-				var childMaxHeight = frameworkElement.MaxHeight;
-				var childMaxWidth = frameworkElement.MaxWidth;
-				var (childMinHeight, childMinWidth) = (frameworkElement as ILayoutOptOut)?.ShouldUseMinSize == false
-					? (0, 0)
-					: (frameworkElement.MinHeight, frameworkElement.MinWidth);
-				var childWidth = frameworkElement.Width;
-				var childHeight = frameworkElement.Height;
-				var childMargin = frameworkElement.Margin;
-
-				var hasChildHeight = !IsNaN(childHeight);
-				var hasChildWidth = !IsNaN(childWidth);
-				var hasChildMaxWidth = !IsInfinity(childMaxWidth) && !IsNaN(childMaxWidth);
-				var hasChildMaxHeight = !IsInfinity(childMaxHeight) && !IsNaN(childMaxHeight);
-				var hasChildMinWidth = childMinWidth > 0.0;
-				var hasChildMinHeight = childMinHeight > 0.0;
-
-				if (
-					childVerticalAlignment != VerticalAlignment.Stretch
-					|| childHorizontalAlignment != HorizontalAlignment.Stretch
-					|| hasChildWidth
-					|| hasChildHeight
-					|| hasChildMaxWidth
-					|| hasChildMaxHeight
-					|| hasChildMinWidth
-					|| hasChildMinHeight
-					)
-				{
-					var desiredSize = LayoutInformation.GetDesiredSize(view);
-
-					// Apply vertical alignment
-					if (
-						childVerticalAlignment != VerticalAlignment.Stretch
-						|| hasChildHeight
-						|| hasChildMaxHeight
-						|| hasChildMinHeight
-					)
-					{
-						var actualHeight = GetActualSize(
-							frame.Height,
-							childVerticalAlignment == VerticalAlignment.Stretch,
-							childMaxHeight,
-							childMinHeight,
-							childHeight,
-							childMargin.Top + childMargin.Bottom,
-							hasChildHeight,
-							hasChildMaxHeight,
-							hasChildMinHeight,
-							desiredSize.Height);
-
-						if (actualHeight == frame.Height)
-						{
-							y = frame.Y; // nothing to align: we're using exactly the available height
-
-						}
-						else
-						{
-							switch (childVerticalAlignment)
-							{
-								case VerticalAlignment.Top:
-									y = frame.Y;
-									break;
-
-								case VerticalAlignment.Bottom:
-									y = frame.Y + frame.Height - actualHeight;
-									break;
-
-								case VerticalAlignment.Stretch:
-									// On UWP, when a control is taking more height than available from
-									// parent, it will be top-aligned when its alignment is Stretch
-									y = frame.Y + Math.Max((frame.Height - actualHeight) / 2d, 0d);
-									break;
-								case VerticalAlignment.Center:
-									y = frame.Y + (frame.Height - actualHeight) / 2d;
-									break;
-							}
-						}
-
-						height = actualHeight;
-					}
-
-					// Apply horizontal alignment
-					if (
-						childHorizontalAlignment != HorizontalAlignment.Stretch
-						|| hasChildWidth
-						|| hasChildMaxWidth
-						|| hasChildMinWidth
-					)
-					{
-						var actualWidth = GetActualSize(
-							frame.Width,
-							childHorizontalAlignment == HorizontalAlignment.Stretch,
-							childMaxWidth,
-							childMinWidth,
-							childWidth,
-							childMargin.Left + childMargin.Right,
-							hasChildWidth,
-							hasChildMaxWidth,
-							hasChildMinWidth,
-							desiredSize.Width);
-
-						if (actualWidth == frame.Width)
-						{
-							x = frame.X; // nothing to align: we're using exactly the available width
-						}
-						else
-						{
-							switch (childHorizontalAlignment)
-							{
-								case HorizontalAlignment.Left:
-									x = frame.X;
-									break;
-
-								case HorizontalAlignment.Right:
-									x = frame.X + frame.Width - actualWidth;
-									break;
-
-								case HorizontalAlignment.Stretch:
-									// On UWP, when a control is taking more width than available from
-									// parent, it will be left-aligned when its alignment is Stretch
-									x = frame.X + Math.Max((frame.Width - actualWidth) / 2d, 0d);
-									break;
-								case HorizontalAlignment.Center:
-									x = frame.X + (frame.Width - actualWidth) / 2d;
-									break;
-							}
-						}
-
-						width = actualWidth;
-					}
-				}
-
-				// Calculate Create layoutFrame and apply child's margins
-				var layoutFrame = new Rect(x, y, width, height).DeflateBy(childMargin);
-
-				// Give opportunity to element to alter arranged size
-				layoutFrame.Size = frameworkElement.AdjustArrange(layoutFrame.Size);
-
-				// Calculate clipped frame.
-				var clippedFrameWithParentOrigin =
-					layoutFrame
-						.IntersectWith(frame.DeflateBy(childMargin))
-					?? Rect.Empty;
-
-				// Rebase the origin of the clipped frame to layout
-				var clippedFrame = new Rect(
-					clippedFrameWithParentOrigin.X - layoutFrame.X,
-					clippedFrameWithParentOrigin.Y - layoutFrame.Y,
-					clippedFrameWithParentOrigin.Width,
-					clippedFrameWithParentOrigin.Height);
-
-				return (layoutFrame, clippedFrame);
-			}
-			else
-			{
-				var layoutFrame = new Rect(
-					x: IsNaN(frame.X) ? 0 : frame.X,
-					y: IsNaN(frame.Y) ? 0 : frame.Y,
-					width: Math.Max(0, IsNaN(frame.Width) ? 0 : frame.Width),
-					height: Math.Max(0, IsNaN(frame.Height) ? 0 : frame.Height)
-				);
-
-				// Clipped frame & layout frame are the same for native elements
-				return (layoutFrame, layoutFrame);
-			}
-		}
-
-		protected virtual void AdjustAlignment(View view, ref HorizontalAlignment childHorizontalAlignment,
-			ref VerticalAlignment childVerticalAlignment)
-		{
-			if (view is Image img && (img.Stretch == Stretch.None || img.Stretch == Stretch.Uniform))
-			{
-				// Image is a special control and is using the Vertical/Horizontal Alignment
-				// to calculate the final position of the image. On UWP, there's no difference
-				// between "Stretch" and "Center": they all behave like "Center", so we need
-				// to do the same here.
-				if (childVerticalAlignment == VerticalAlignment.Stretch)
-				{
-					childVerticalAlignment = VerticalAlignment.Center;
-				}
-
-				if (childHorizontalAlignment == HorizontalAlignment.Stretch)
-				{
-					childHorizontalAlignment = HorizontalAlignment.Center;
-				}
-			}
-		}
-
-		private double GetActualSize(
-			double frameSize,
-			bool isStretch,
-			double childMaxSize,
-			double childMinSize,
-			double childSize,
-			double childMarginSize,
-			bool hasChildSize,
-			bool hasChildMaxSize,
-			bool hasChildMinSize,
-			double desiredSize)
-		{
-			var min = hasChildMinSize ? childMinSize + childMarginSize : NegativeInfinity;
-			var max = hasChildMaxSize ? childMaxSize + childMarginSize : PositiveInfinity;
-			if (!hasChildSize)
-			{
-				childSize = isStretch
-					? frameSize
-					: desiredSize; // desired size always include margin, so no need to calculate it here
-			}
-			else
-			{
-				childSize += childMarginSize;
-			}
-
-			return Math.Max(
-				Math.Min(
-					Math.Min(childSize, frameSize),
-					max),
-				min);
-		}
-
-		/// <summary>
-		/// Measures the specified child.
-		/// </summary>
-		/// <param name="view">The view to measure</param>
-		/// <param name="slotSize">The maximum size the child can use.</param>
-		/// <returns>The size the view requires.</returns>
-		/// <remarks>
-		/// Provides the ability for external implementations to measure children.
-		/// Mainly used for compatibility with existing WPF/WinRT implementations.
-		/// </remarks>
-		void ILayouter.ArrangeChild(View view, Rect frame)
-		{
-			ArrangeChild(view, frame);
-		}
-
-		/// <summary>
-		/// Arranges the specified view.
-		/// </summary>
-		/// <param name="view">The view to arrange</param>
-		/// <param name="frame">The frame available for the child.</param>
-		/// <remarks>
-		/// Provides the ability for external implementations to measure children.
-		/// Mainly used for compatibility with existing WPF/WinRT implementations.
-		/// </remarks>
-		Size ILayouter.MeasureChild(View view, Size slotSize)
-		{
-			return MeasureChild(view, slotSize);
-		}
-
-		private string LoggingOwnerTypeName => ((object)Panel ?? this).GetType().Name;
-
-		public override string ToString() => $"[{LoggingOwnerTypeName}.Layouter]" + (string.IsNullOrEmpty(Panel?.Name) ? default : $"(name='{Panel.Name}')");
-	}
-}
-#endif
diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.iOS.cs
deleted file mode 100644
index fe455f4240b1..000000000000
--- a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.iOS.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Uno.Extensions;
-using Uno;
-using Uno.UI;
-using Uno.Foundation.Logging;
-using Uno.Collections;
-using Microsoft.UI.Xaml.Media;
-using Windows.Foundation;
-using View = UIKit.UIView;
-using UIKit;
-using CoreGraphics;
-using Uno.Disposables;
-using ObjCRuntime;
-
-namespace Microsoft.UI.Xaml.Controls
-{
-	abstract partial class Layouter
-	{
-		public IEnumerable<View> GetChildren()
-		{
-			return (Panel as UIView).GetChildren();
-		}
-
-		protected Size MeasureChildOverride(View view, Size slotSize)
-		{
-			var ret = view
-				.SizeThatFits(slotSize.LogicalToPhysicalPixels())
-				.PhysicalToLogicalPixels()
-				.ToFoundationSize();
-
-			// With iOS, a child may return a size that fits that is larger than the suggested size.
-			// We don't want that with respects to the Xaml model, so we cap the size to the input constraints.
-
-			if (!(view is FrameworkElement) && view is IFrameworkElement ife)
-			{
-				if (!(view is Image)) // Except for Image
-				{
-					// If the child is not a FrameworkElement, part of the "Measure"
-					// phase must be done by the parent element's layouter.
-					// Here, it means adding the margin to the measured size.
-					ret = ret.Add(ife.Margin);
-				}
-			}
-
-			ret.Width = double.IsNaN(ret.Width) ? double.PositiveInfinity : Math.Min(slotSize.Width, ret.Width);
-			ret.Height = double.IsNaN(ret.Height) ? double.PositiveInfinity : Math.Min(slotSize.Height, ret.Height);
-
-			return ret;
-		}
-
-		protected void ArrangeChildOverride(View view, Rect frame)
-		{
-			var nativeFrame = ViewHelper.LogicalToPhysicalPixels(frame);
-
-			if (nativeFrame != view.Frame)
-			{
-				LogArrange(view, nativeFrame);
-
-				using (SettingFrame(view))
-				{
-					view.Frame = nativeFrame;
-				}
-			}
-		}
-
-		private void LogArrange(View view, CGRect frame)
-		{
-			if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-			{
-				LogArrange(view, (Rect)frame);
-			}
-		}
-
-		/// <summary>
-		/// Handle the native <see cref="View.Transform"/> in the (rare) case that this is a non-IFrameworkElement view with a 
-		/// non-identity transform.
-		/// </summary>
-		private IDisposable SettingFrame(View view)
-		{
-			if (view is IFrameworkElement)
-			{
-				// This is handled directly in IFrameworkElement.Frame setter
-				return null;
-			}
-
-			if (view.Transform.IsIdentity)
-			{
-				// Transform is identity anyway
-				return null;
-			}
-
-			// If UIView.Transform is not identity, then modifying the frame will give undefined behavior. (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/transform)
-			// We have either already applied the transform to the new frame, or we will reset the transform straight after.
-			var transform = view.Transform;
-			view.Transform = CGAffineTransform.MakeIdentity();
-			return Disposable.Create(reapplyTransform);
-
-			void reapplyTransform()
-			{
-				view.Transform = transform;
-			}
-		}
-	}
-}
diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.macOS.cs
deleted file mode 100644
index 331f0dfb5898..000000000000
--- a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.macOS.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Uno.Extensions;
-using Uno;
-using Uno.UI;
-using Uno.Foundation.Logging;
-using Uno.Collections;
-using Microsoft.UI.Xaml.Media;
-using Windows.Foundation;
-
-using View = AppKit.NSView;
-using AppKit;
-using CoreGraphics;
-using Uno.Disposables;
-using CoreAnimation;
-using ObjCRuntime;
-
-namespace Microsoft.UI.Xaml.Controls
-{
-	abstract partial class Layouter
-	{
-		public IEnumerable<View> GetChildren()
-		{
-			return (Panel as NSView).GetChildren();
-		}
-
-		protected Size MeasureChildOverride(View view, Size slotSize)
-		{
-			var ret = view
-				.SizeThatFits(slotSize.LogicalToPhysicalPixels())
-				.PhysicalToLogicalPixels()
-				.ToFoundationSize();
-
-			// With iOS, a child may return a size that fits that is larger than the suggested size.
-			// We don't want that with respects to the Xaml model, so we cap the size to the input constraints.
-			ret.Width = double.IsNaN(ret.Width) ? double.PositiveInfinity : Math.Min(slotSize.Width, ret.Width);
-			ret.Height = double.IsNaN(ret.Height) ? double.PositiveInfinity : Math.Min(slotSize.Height, ret.Height);
-
-			return ret;
-		}
-
-		protected void ArrangeChildOverride(View view, Rect frame)
-		{
-			var nativeFrame = ViewHelper.LogicalToPhysicalPixels(frame);
-
-			if (nativeFrame != view.Frame)
-			{
-				LogArrange(view, nativeFrame);
-
-				using (SettingFrame(view))
-				{
-					view.Frame = nativeFrame;
-
-					UpdateClip(view);
-				}
-			}
-		}
-
-		private void LogArrange(View view, CGRect frame)
-		{
-			if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-			{
-				LogArrange(view, (Rect)frame);
-			}
-		}
-
-		private static void UpdateClip(View view)
-		{
-			// TODO
-
-			//if (!FeatureConfiguration.UIElement.UseLegacyClipping)
-			//{
-			//	UIElement.UpdateMask(view, view.Superview);
-
-			//	foreach (var child in view.GetChildren())
-			//	{
-			//		UIElement.UpdateMask(child, view);
-			//	}
-			//}
-		}
-
-		/// <summary>
-		/// Handle the native <see cref="View.Transform"/> in the (rare) case that this is a non-IFrameworkElement view with a 
-		/// non-identity transform.
-		/// </summary>
-		private IDisposable SettingFrame(View view)
-		{
-			if (view is IFrameworkElement)
-			{
-				// This is handled directly in IFrameworkElement.Frame setter
-				return null;
-			}
-
-			var layer = view.Layer;
-			if (layer == null)
-			{
-				// Transform is identity anyway, or Layer is null
-				return null;
-			}
-
-			// If NSView.Transform is not identity, then modifying the frame will give undefined behavior. (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/NSView/transform)
-			// We have either already applied the transform to the new frame, or we will reset the transform straight after.
-			var transform = layer.Transform;
-			if (transform.IsIdentity)
-			{
-				return null;
-			}
-			transform = CATransform3D.Identity;
-			return Disposable.Create(reapplyTransform);
-
-			void reapplyTransform()
-			{
-				layer.Transform = transform;
-			}
-		}
-	}
-}
diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.unittests.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.unittests.cs
deleted file mode 100644
index 75d880e813b2..000000000000
--- a/src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.unittests.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Windows.Foundation;
-using System.Linq;
-using Uno.Disposables;
-using System.Text;
-using System.Threading.Tasks;
-using Uno.Extensions;
-using Uno;
-using Uno.Foundation.Logging;
-using View = Microsoft.UI.Xaml.UIElement;
-using Microsoft.UI.Xaml.Media;
-using Microsoft.UI.Xaml.Controls.Primitives;
-
-namespace Microsoft.UI.Xaml.Controls
-{
-	partial class Layouter : ILayouter
-	{
-		protected Size MeasureChildOverride(View view, Size slotSize)
-		{
-			view.Measure(slotSize);
-
-			return view.DesiredSize;
-		}
-
-		protected void ArrangeChildOverride(View view, Rect frame)
-		{
-			view.Arranged = frame;
-			view.LayoutSlotWithMarginsAndAlignments = frame;
-
-			LayoutInformation.SetLayoutSlot(view, frame);
-		}
-
-		protected Size DesiredChildSize(View view)
-		{
-			return view.DesiredSize;
-		}
-	}
-}
diff --git a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseSource.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseSource.iOS.cs
index e8de2e5d73dd..e066cacf3683 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseSource.iOS.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseSource.iOS.cs
@@ -279,13 +279,6 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS
 					// Normally this happens when the SelectorItem.Content is set, but there's an edge case where after a refresh, a
 					// container can be dequeued which happens to have had exactly the same DataContext as the new item.
 					cell.ClearMeasuredSize();
-
-					// Ensure ClippedFrame from a previous recycled item doesn't persist which can happen in some cases,
-					// and cause it to be clipped when either axis was smaller.
-					if (cell.Content is { } contentControl)
-					{
-						contentControl.ClippedFrame = null;
-					}
 				}
 
 				Owner?.XamlParent?.TryLoadMoreItems(index);
@@ -672,7 +665,8 @@ private CGSize GetTemplateSize(DataTemplate dataTemplate, NSString elementKind,
 					Owner.XamlParent.AddSubview(BlockLayout);
 					BlockLayout.AddSubview(container);
 					// Measure with PositiveInfinity rather than MaxValue, since some views handle this better.
-					size = Owner.NativeLayout.Layouter.MeasureChild(container, availableSize);
+					container.Measure(availableSize);
+					size = container.DesiredSize;
 
 					if ((size.Height > nfloat.MaxValue / 2 || size.Width > nfloat.MaxValue / 2) &&
 						this.Log().IsEnabled(LogLevel.Warning)
@@ -764,7 +758,6 @@ public NativeListViewBase Owner
 
 		private Orientation ScrollOrientation => Owner.NativeLayout.ScrollOrientation;
 		private bool SupportsDynamicItemSizes => Owner.NativeLayout.SupportsDynamicItemSizes;
-		private ILayouter Layouter => Owner.NativeLayout.Layouter;
 
 		internal string ElementKind { get; set; }
 
@@ -952,7 +945,8 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin
 					{
 						using (InterceptSetNeedsLayout())
 						{
-							_measuredContentSize = Layouter.MeasureChild(Content, availableSize);
+							Content.Measure(availableSize);
+							_measuredContentSize = Content.DesiredSize;
 						}
 					}
 					else
@@ -962,7 +956,8 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin
 							//Attach temporarily, because some Uno control (eg ItemsControl) are only measured correctly after MovedToWindow has been called
 							InterceptSetNeedsLayout();
 							Owner.XamlParent.AddSubview(this);
-							_measuredContentSize = Layouter.MeasureChild(Content, availableSize);
+							Content.Measure(availableSize);
+							_measuredContentSize = Content.DesiredSize;
 						}
 						finally
 						{
@@ -1056,7 +1051,7 @@ public override void LayoutSubviews()
 
 			if (Content != null)
 			{
-				Layouter.ArrangeChild(Content, new Rect(0, 0, (float)size.Width, (float)size.Height));
+				Content.Arrange(new Rect(0, 0, (float)size.Width, (float)size.Height));
 
 				// The item has to be arranged relative to this internal container (at 0,0),
 				// but doing this the LayoutSlot[WithMargins] has been updated, 
diff --git a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs
index f3ad99b066ad..0b6507ed495c 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.Android.cs
@@ -1019,7 +1019,7 @@ private Size TryMeasureChild(View child, Size slotSize, ViewType viewType)
 
 			if (child.IsLayoutRequested || slotSize != previousAvailableSize)
 			{
-				var size = _layouter.MeasureChild(child, slotSize);
+				var size = slotSize; // TODO _layouter.MeasureChild(child, slotSize);
 
 				if (ShouldApplyChildStretch)
 				{
@@ -1094,7 +1094,7 @@ protected void LayoutChild(View child, GeneratorDirection direction, int extentO
 				top = logicalBreadthOffset;
 			}
 			var frame = new global::Windows.Foundation.Rect(new global::Windows.Foundation.Point(left, top), size);
-			_layouter.ArrangeChild(child, frame);
+			//_layouter.ArrangeChild(child, frame);
 
 			// Due to conversions between physical and logical coordinates, the actual child end can differ from the end we sent to the layouter by a little bit.
 			Debug.Assert(direction == GeneratorDirection.Forward || Math.Abs(GetChildEndWithMargin(child) - extentOffset) < 2, GetAssertMessage("Extent offset not applied correctly"));
diff --git a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.cs b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.cs
index 2539e39015c0..7b2658ce2fd3 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/VirtualizingPanelLayout.cs
@@ -34,8 +34,8 @@ protected enum RelativeHeaderPlacement { Inline, Adjacent }
 		/// <remarks>For <see cref="ItemsStackPanel"/> layouting this is identical to <see cref="Orientation"/> but for <see cref="ItemsWrapGrid"/> it is the opposite of <see cref="Orientation"/>.</remarks>
 		public abstract Orientation ScrollOrientation { get; }
 #if !UNO_REFERENCE_API
-		private protected readonly ILayouter _layouter = new VirtualizingPanelLayouter();
-		internal ILayouter Layouter => _layouter;
+		//private protected readonly ILayouter _layouter = new VirtualizingPanelLayouter();
+		//internal ILayouter Layouter => _layouter;
 #endif
 
 #pragma warning disable 67 // Unused member
@@ -282,32 +282,32 @@ public static (TSource Item, TComparable Value) MinWithSelector<TSource, TCompar
 		}
 
 #if !UNO_REFERENCE_API
-		private class VirtualizingPanelLayouter : Layouter
-		{
-
-			public VirtualizingPanelLayouter() : base(null)
-			{
-
-			}
-			protected override string Name => "VirtualizingPanelLayout";
-
-			protected override Size ArrangeOverride(Size finalSize)
-			{
-				throw new NotSupportedException($"{nameof(VirtualizingPanelLayouter)} is only used for measuring and arranging child views.");
-			}
-
-#if __ANDROID__
-			protected override void MeasureChild(Android.Views.View view, int widthSpec, int heightSpec)
-			{
-				view.Measure(widthSpec, heightSpec);
-			}
-#endif
-
-			protected override Size MeasureOverride(Size availableSize)
-			{
-				throw new NotSupportedException($"{nameof(VirtualizingPanelLayouter)} is only used for measuring and arranging child views.");
-			}
-		}
+		//		private class VirtualizingPanelLayouter : Layouter
+		//		{
+
+		//			public VirtualizingPanelLayouter() : base(null)
+		//			{
+
+		//			}
+		//			protected override string Name => "VirtualizingPanelLayout";
+
+		//			protected override Size ArrangeOverride(Size finalSize)
+		//			{
+		//				throw new NotSupportedException($"{nameof(VirtualizingPanelLayouter)} is only used for measuring and arranging child views.");
+		//			}
+
+		//#if __ANDROID__
+		//			protected override void MeasureChild(Android.Views.View view, int widthSpec, int heightSpec)
+		//			{
+		//				view.Measure(widthSpec, heightSpec);
+		//			}
+		//#endif
+
+		//			protected override Size MeasureOverride(Size availableSize)
+		//			{
+		//				throw new NotSupportedException($"{nameof(VirtualizingPanelLayouter)} is only used for measuring and arranging child views.");
+		//			}
+		//		}
 #endif
 	}
 }
diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Android.cs
index c96d78b7aace..ee68877cfda8 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Android.cs
@@ -46,7 +46,7 @@ public ScrollBarVisibility HorizontalScrollBarVisibility
 			}
 		}
 
-		private ILayouter _layouter;
+		//private ILayouter _layouter;
 		private readonly WeakReference<ScrollViewer> _scrollViewer;
 
 		public NativeScrollContentPresenter(ScrollViewer scroller) : this()
@@ -65,7 +65,7 @@ public NativeScrollContentPresenter()
 			SetClipChildren(false);
 			ScrollBarStyle = ScrollbarStyles.OutsideOverlay; // prevents padding from affecting scrollbar position
 
-			_layouter = new ScrollViewerLayouter(this);
+			//_layouter = new ScrollViewerLayouter(this);
 		}
 
 		private void InitializeScrollbars()
@@ -103,21 +103,89 @@ partial void OnContentChanged(View previousView, View newView)
 			}
 		}
 
-		ILayouter ILayouterElement.Layouter => _layouter;
-		Size ILayouterElement.LastAvailableSize => LayoutInformation.GetAvailableSize(this);
-		bool ILayouterElement.IsMeasureDirty => true;
-		bool ILayouterElement.IsFirstMeasureDoneAndManagedElement => false;
-		bool ILayouterElement.StretchAffectsMeasure => false;
-		bool ILayouterElement.IsMeasureDirtyPathDisabled => true;
-
 		protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
 		{
-			((ILayouterElement)this).OnMeasureInternal(widthMeasureSpec, heightMeasureSpec);
+			base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
 		}
 
-		void ILayouterElement.SetMeasuredDimensionInternal(int width, int height)
+		Size ILayouterElement.Measure(Size availableSize)
 		{
-			SetMeasuredDimension(width, height);
+			var child = this.GetChildren().FirstOrDefault();
+
+			var desiredChildSize = default(Size);
+			if (child != null)
+			{
+				var scrollSpace = availableSize;
+				if (VerticalScrollBarVisibility != ScrollBarVisibility.Disabled)
+				{
+					scrollSpace.Height = double.PositiveInfinity;
+				}
+				if (HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled)
+				{
+					scrollSpace.Width = double.PositiveInfinity;
+				}
+
+				if (child is UIElement childAsUIElement)
+				{
+					var childMargin = (child as FrameworkElement)?.Margin ?? Thickness.Empty;
+					SetChildMargin(childMargin);
+
+					childAsUIElement.Measure(scrollSpace);
+					desiredChildSize = childAsUIElement.DesiredSize;
+				}
+				else if (child is ILayouterElement layouterElement)
+				{
+					desiredChildSize = layouterElement.Measure(scrollSpace);
+				}
+				else
+				{
+					// TODO:
+				}
+
+				// Give opportunity to the the content to define the viewport size itself
+				(child as ICustomScrollInfo)?.ApplyViewport(ref desiredChildSize);
+			}
+
+			return desiredChildSize;
+		}
+
+		void ILayouterElement.Arrange(Rect finalRect)
+		{
+			var child = this.GetChildren().FirstOrDefault();
+			if (child != null)
+			{
+				var desiredChildSize = LayoutInformation.GetDesiredSize(child);
+
+				var occludedPadding = _padding;
+				var slotSize = finalRect.Size;
+				slotSize.Width -= occludedPadding.Left + occludedPadding.Right;
+				slotSize.Height -= occludedPadding.Top + occludedPadding.Bottom;
+
+				var width = Math.Max(slotSize.Width, desiredChildSize.Width);
+				var height = Math.Max(slotSize.Height, desiredChildSize.Height);
+
+				if (child is UIElement childAsUIElement)
+				{
+					childAsUIElement.Arrange(new Rect(0, 0, width, height));
+				}
+				else if (child is ILayouterElement layouterElement)
+				{
+					layouterElement.Arrange(new Rect(0, 0, width, height));
+				}
+				else
+				{
+					// TODO:
+				}
+
+				ScrollOwner?.TryApplyPendingScrollTo();
+
+				// Give opportunity to the the content to define the viewport size itself
+				(child as ICustomScrollInfo)?.ApplyViewport(ref slotSize);
+
+				var logicalRect = new Rect(0, 0, slotSize.Width, slotSize.Height);
+				var physical = logicalRect.LogicalToPhysicalPixels();
+				this.Layout((int)physical.Left, (int)physical.Top, (int)physical.Right, (int)physical.Bottom);
+			}
 		}
 
 		partial void OnLayoutPartial(bool changed, int left, int top, int right, int bottom)
@@ -129,7 +197,7 @@ partial void OnLayoutPartial(bool changed, int left, int top, int right, int bot
 			// may leave the default ScrollViewer implementation place 
 			// the child at an invalid location when the visibility changes.
 
-			_layouter.Arrange(newSize);
+			//_layouter.Arrange(newSize);
 
 			// base.OnLayout is not invoked in the mixin to allow for the clipping algorithms
 			base.OnLayout(changed, left, top, right, bottom);
@@ -149,83 +217,6 @@ private void UpdateScrollSettings()
 			IsScrollingEnabled = verticalScrollEnabled || horizontalScrollEnabled;
 		}
 
-		private class ScrollViewerLayouter : Layouter
-		{
-			public ScrollViewerLayouter(NativeScrollContentPresenter view) : base(view)
-			{
-			}
-
-			private NativeScrollContentPresenter ScrollContentPresenter => Panel as NativeScrollContentPresenter;
-
-			protected override void MeasureChild(View child, int widthSpec, int heightSpec)
-			{
-				var childMargin = (child as FrameworkElement)?.Margin ?? Thickness.Empty;
-				ScrollContentPresenter.SetChildMargin(childMargin);
-
-				this.GetChildren().FirstOrDefault()?.Measure(widthSpec, heightSpec);
-			}
-
-			protected override Size MeasureOverride(Size availableSize)
-			{
-				var child = this.GetChildren().FirstOrDefault();
-
-				var desiredChildSize = default(Size);
-				if (child != null)
-				{
-					var scrollSpace = availableSize;
-					if (ScrollContentPresenter.VerticalScrollBarVisibility != ScrollBarVisibility.Disabled)
-					{
-						scrollSpace.Height = double.PositiveInfinity;
-					}
-					if (ScrollContentPresenter.HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled)
-					{
-						scrollSpace.Width = double.PositiveInfinity;
-					}
-
-					desiredChildSize = MeasureChild(child, scrollSpace);
-
-					// Give opportunity to the the content to define the viewport size itself
-					(child as ICustomScrollInfo)?.ApplyViewport(ref desiredChildSize);
-				}
-
-				return desiredChildSize;
-			}
-
-			protected override Size ArrangeOverride(Size slotSize)
-			{
-				var child = this.GetChildren().FirstOrDefault();
-
-				if (child != null)
-				{
-					var desiredChildSize = LayoutInformation.GetDesiredSize(child);
-
-					var occludedPadding = ScrollContentPresenter._padding;
-					slotSize.Width -= occludedPadding.Left + occludedPadding.Right;
-					slotSize.Height -= occludedPadding.Top + occludedPadding.Bottom;
-
-					var width = Math.Max(slotSize.Width, desiredChildSize.Width);
-					var height = Math.Max(slotSize.Height, desiredChildSize.Height);
-
-					ArrangeChild(child, new Rect(
-						0,
-						0,
-						width,
-						height
-					));
-
-					ScrollContentPresenter.ScrollOwner?.TryApplyPendingScrollTo();
-
-					// Give opportunity to the the content to define the viewport size itself
-					(child as ICustomScrollInfo)?.ApplyViewport(ref slotSize);
-
-				}
-
-				return slotSize;
-			}
-
-			protected override string Name => Panel.Name;
-		}
-
 		#region Managed to native
 		private Thickness _padding;
 		Thickness INativeScrollContentPresenter.Padding
diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.Android.cs b/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.Android.cs
index db97c127484a..21dbfc4ff3a9 100644
--- a/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.Android.cs
@@ -287,13 +287,13 @@ private Java.Lang.ICharSequence GetTextFormatted()
 
 		#region Layout
 
-		internal protected override void OnInvalidateMeasure()
-		{
-			base.OnInvalidateMeasure();
+		//internal protected override void OnInvalidateMeasure()
+		//{
+		//	base.OnInvalidateMeasure();
 
-			// We want to invalidate both the layout and the rendering
-			Invalidate();
-		}
+		//	// We want to invalidate both the layout and the rendering
+		//	Invalidate();
+		//}
 
 		protected override Size MeasureOverride(Size availableSize)
 		{
diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.iOS.cs
index abe6cd225217..656c688681c8 100644
--- a/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.iOS.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.iOS.cs
@@ -76,15 +76,15 @@ public override void Draw(CGRect rect)
 			}
 		}
 
-		/// <summary>
-		/// Invalidates the last cached measure
-		/// </summary>
-		protected internal override void OnInvalidateMeasure()
-		{
-			base.OnInvalidateMeasure();
-			SetNeedsDisplay();
-			_measureInvalidated = true;
-		}
+		///// <summary>
+		///// Invalidates the last cached measure
+		///// </summary>
+		//protected internal override void OnInvalidateMeasure()
+		//{
+		//	base.OnInvalidateMeasure();
+		//	SetNeedsDisplay();
+		//	_measureInvalidated = true;
+		//}
 
 		#region Layout
 
diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.Android.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.Android.cs
index 97c1bd3d056e..c24afcea4287 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkElement.Android.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkElement.Android.cs
@@ -236,12 +236,10 @@ internal override bool GetDefaultValue2(DependencyProperty property, out object
 
 		protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
 		{
-			((ILayouterElement)this).OnMeasureInternal(widthMeasureSpec, heightMeasureSpec);
-		}
+			base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
 
-		void ILayouterElement.SetMeasuredDimensionInternal(int width, int height)
-		{
-			SetMeasuredDimension(width, height);
+			global::System.Diagnostics.Debug.WriteLine($"Measured {this}: {ViewHelper.PhysicalToLogicalPixels(MeasuredWidth)}x{ViewHelper.PhysicalToLogicalPixels(MeasuredHeight)}");
+			//((ILayouterElement)this).OnMeasureInternal(widthMeasureSpec, heightMeasureSpec);
 		}
 
 		protected override void OnLayoutCore(bool changed, int left, int top, int right, int bottom, bool localIsLayoutRequested)
@@ -250,56 +248,56 @@ protected override void OnLayoutCore(bool changed, int left, int top, int right,
 			{
 				base.OnLayoutCore(changed, left, top, right, bottom, localIsLayoutRequested);
 
-				Rect finalRect;
-				if (TransientArrangeFinalRect is Rect tafr)
-				{
-					// If the parent element is from managed code,
-					// we can recover the "Arrange" with double accuracy.
-					// We use that because the conversion to android's "int" is loosing too much precision.
-					finalRect = tafr;
-				}
-				else
-				{
-					// Here the "arrange" is coming from a native element,
-					// so we convert those measurements to logical ones.
-					finalRect = new Rect(left, top, right - left, bottom - top).PhysicalToLogicalPixels();
-
-					// We also need to set the LayoutSlot as it was not set by the parent.
-					// Note: This is only an approximation of the LayoutSlot as margin and alignment might already been applied at this point.
-					LayoutInformation.SetLayoutSlot(this, finalRect);
-					LayoutSlotWithMarginsAndAlignments = finalRect;
-				}
-
-				if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
-				{
-					this.Log().DebugFormat(
-						"[{0}/{1}] OnLayoutCore({2}, {3}, {4}, {5}) (parent: {5},{6})",
-						GetType(),
-						Name,
-						left, top, right, bottom,
-						MeasuredWidth,
-						MeasuredHeight
-					);
-				}
-
-				if (
-					// If the layout has changed, but the final size has not, this is just a translation.
-					// So unless there was a layout requested, we can skip arranging the children.
-					(changed && _lastLayoutSize != finalRect.Size)
-
-					// Even if nothing changed, but a layout was requested, arrange the children.
-					// Use the copy grabbed from the native invocation to avoid an additional interop call
-					|| localIsLayoutRequested
-				)
-				{
-					_lastLayoutSize = finalRect.Size;
-
-					OnBeforeArrange();
-
-					_layouter.Arrange(finalRect);
-
-					OnAfterArrange();
-				}
+				//Rect finalRect;
+				//if (TransientArrangeFinalRect is Rect tafr)
+				//{
+				//	// If the parent element is from managed code,
+				//	// we can recover the "Arrange" with double accuracy.
+				//	// We use that because the conversion to android's "int" is loosing too much precision.
+				//	finalRect = tafr;
+				//}
+				//else
+				//{
+				//	// Here the "arrange" is coming from a native element,
+				//	// so we convert those measurements to logical ones.
+				//	finalRect = new Rect(left, top, right - left, bottom - top).PhysicalToLogicalPixels();
+
+				//	// We also need to set the LayoutSlot as it was not set by the parent.
+				//	// Note: This is only an approximation of the LayoutSlot as margin and alignment might already been applied at this point.
+				//	LayoutInformation.SetLayoutSlot(this, finalRect);
+				//	LayoutSlotWithMarginsAndAlignments = finalRect;
+				//}
+
+				//if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
+				//{
+				//	this.Log().DebugFormat(
+				//		"[{0}/{1}] OnLayoutCore({2}, {3}, {4}, {5}) (parent: {5},{6})",
+				//		GetType(),
+				//		Name,
+				//		left, top, right, bottom,
+				//		MeasuredWidth,
+				//		MeasuredHeight
+				//	);
+				//}
+
+				//if (
+				//	// If the layout has changed, but the final size has not, this is just a translation.
+				//	// So unless there was a layout requested, we can skip arranging the children.
+				//	(changed && _lastLayoutSize != finalRect.Size)
+
+				//	// Even if nothing changed, but a layout was requested, arrange the children.
+				//	// Use the copy grabbed from the native invocation to avoid an additional interop call
+				//	|| localIsLayoutRequested
+				//)
+				//{
+				//	_lastLayoutSize = finalRect.Size;
+
+				//	OnBeforeArrange();
+
+				//	_layouter.Arrange(finalRect);
+
+				//	OnAfterArrange();
+				//}
 			}
 			catch (Exception e)
 			{
diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.crossruntime.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.crossruntime.cs
index 78684e5c86b8..6ec188ab6969 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.crossruntime.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.crossruntime.cs
@@ -22,11 +22,8 @@ namespace Microsoft.UI.Xaml
 {
 	public partial class FrameworkElement
 	{
-		private readonly static IEventProvider _trace = Tracing.Get(FrameworkElement.TraceProvider.Id);
-
 		private bool m_firedLoadingEvent;
 
-		private const double SIZE_EPSILON = 0.05d;
 		private readonly Size MaxSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
 
 		private protected string DepthIndentation
@@ -127,254 +124,6 @@ void InvokeLoadedWithTry()
 
 		partial void OnLoadedPartial();
 
-		internal sealed override void MeasureCore(Size availableSize)
-		{
-			if (_trace.IsEnabled)
-			{
-				/// <remarks>
-				/// This method contains or is called by a try/catch containing method and
-				/// can be significantly slower than other methods as a result on WebAssembly.
-				/// See https://github.com/dotnet/runtime/issues/56309
-				/// </remarks>
-				void MeasureCoreWithTrace(Size availableSize)
-				{
-					var traceActivity = _trace.WriteEventActivity(
-										TraceProvider.FrameworkElement_MeasureStart,
-										TraceProvider.FrameworkElement_MeasureStop,
-										new object[] { GetType().Name, this.GetDependencyObjectId(), Name, availableSize.ToString() }
-									);
-
-					using (traceActivity)
-					{
-						InnerMeasureCore(availableSize);
-					}
-				}
-
-				MeasureCoreWithTrace(availableSize);
-			}
-			else
-			{
-				// This method is split in two functions to avoid the dynCalls
-				// invocations generation for mono-wasm AOT inside of try/catch/finally blocks.
-				InnerMeasureCore(availableSize);
-			}
-
-		}
-
-		private void InnerMeasureCore(Size availableSize)
-		{
-			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
-			{
-				this.Log().LogWarning($"[LayoutCycleTracing] Measuring {this},{this.GetDebugName()} with availableSize {availableSize}.");
-			}
-
-			// Uno TODO
-			//CLayoutManager* pLayoutManager = VisualTree::GetLayoutManagerForElement(this);
-			//bool bInLayoutTransition = pLayoutManager ? pLayoutManager->GetTransitioningElement() == this : false;
-
-			Size frameworkAvailableSize = default;
-			double minWidth = 0.0f;
-			double maxWidth = 0.0f;
-			double minHeight = 0.0f;
-			double maxHeight = 0.0f;
-
-			double clippedDesiredWidth;
-			double clippedDesiredHeight;
-
-			double marginWidth = 0.0f;
-			double marginHeight = 0.0f;
-
-			//bool bTemplateApplied = false;
-
-			RaiseLoadingEventIfNeeded();
-
-			//if (!bInLayoutTransition)
-			{
-				// Templates should be applied here.
-				//bTemplateApplied = InvokeApplyTemplate();
-
-				// TODO: BEGIN Uno specific
-				if (this is Control thisAsControl)
-				{
-					thisAsControl.ApplyTemplate();
-
-					// Update bindings to ensure resources defined
-					// in visual parents get applied.
-					this.UpdateResourceBindings();
-				}
-				// TODO: END Uno specific
-
-				// Subtract the margins from the available size
-				var margin = Margin;
-				marginWidth = margin.Left + margin.Right;
-				marginHeight = margin.Top + margin.Bottom;
-
-				// We check to see if availableSize.width and availableSize.height are finite since that will
-				// also protect against NaN getting in.
-				frameworkAvailableSize.Width = double.IsFinite(availableSize.Width) ? Math.Max(availableSize.Width - marginWidth, 0) : double.PositiveInfinity;
-				frameworkAvailableSize.Height = double.IsFinite(availableSize.Height) ? Math.Max(availableSize.Height - marginHeight, 0) : double.PositiveInfinity;
-
-				// Layout transforms would get processed here.
-
-				// Adjust available size by Min/Max Width/Height
-
-				var (minSize, maxSize) = this.GetMinMax();
-				minWidth = minSize.Width;
-				minHeight = minSize.Height;
-				maxWidth = maxSize.Width;
-				maxHeight = maxSize.Height;
-
-				frameworkAvailableSize.Width = Math.Max(minWidth, Math.Min(frameworkAvailableSize.Width, maxWidth));
-				frameworkAvailableSize.Height = Math.Max(minHeight, Math.Min(frameworkAvailableSize.Height, maxHeight));
-			}
-			//else
-			//{
-			//	// when in a transition, just take the passed in constraint without considering the above
-			//	frameworkAvailableSize = availableSize;
-			//}
-
-			var desiredSize = MeasureOverride(frameworkAvailableSize);
-
-			// We need to round now since we save the values off, and use them to determine
-			// if a layout clip will be applied.
-			if (GetUseLayoutRounding())
-			{
-				desiredSize.Width = LayoutRound(desiredSize.Width);
-				desiredSize.Height = LayoutRound(desiredSize.Height);
-
-			}
-
-			//if (!bInLayoutTransition)
-			{
-				// Maximize desired size with user provided min size. It's also possible that MeasureOverride returned NaN for either
-				// width or height, in which case we should use the min size as well.
-				desiredSize.Width = Math.Max(desiredSize.Width, minWidth);
-				if (double.IsNaN(desiredSize.Width))
-				{
-					desiredSize.Width = minWidth;
-				}
-				desiredSize.Height = Math.Max(desiredSize.Height, minHeight);
-				if (double.IsNaN(desiredSize.Height))
-				{
-					desiredSize.Height = minHeight;
-				}
-
-				// We need to round now since we save the values off, and use them to determine
-				// if a layout clip will be applied.
-
-				if (GetUseLayoutRounding())
-				{
-					desiredSize.Width = LayoutRound(desiredSize.Width);
-					desiredSize.Height = LayoutRound(desiredSize.Height);
-				}
-
-				// Here is the "true minimum" desired size - the one that is
-				// for sure enough for the control to render its content.
-				EnsureLayoutStorage();
-				m_unclippedDesiredSize = desiredSize;
-
-				// More layout transforms processing here.
-
-				if (desiredSize.Width > maxWidth)
-				{
-					desiredSize.Width = maxWidth;
-				}
-
-				if (desiredSize.Height > maxHeight)
-				{
-					desiredSize.Height = maxHeight;
-				}
-
-				// Transform desired size to layout slot space (placeholder for when we do layout transforms)
-
-				// Layout round the margins too. This corresponds to the behavior in ArrangeCore, where we check the unclipped desired
-				// size against available space minus the rounded margin. This also prevents a bug where MeasureCore adds the unrounded
-				// margin (e.g. 14) to the desired size (e.g. 55.56) and rounds the final result (69.56 rounded to 69.33 under 2.25x scale),
-				// then ArrangeCore takes that rounded result (i.e. 69.33), subtracts the unrounded margin (i.e. 14) and ends up with a
-				// size smaller than the desired size (69.33 - 14 = 55.33 < 55.56). This ends up putting a layout clip on an element that
-				// doesn't need one, and causes big problems if the element is the scrollable extent of a carousel panel.
-				double roundedMarginWidth = marginWidth;
-				double roundedMarginHeight = marginHeight;
-				if (GetUseLayoutRounding())
-				{
-					roundedMarginWidth = LayoutRound(marginWidth);
-					roundedMarginHeight = LayoutRound(marginHeight);
-				}
-
-				//  Because of negative margins, clipped desired size may be negative.
-				//  Need to keep it as XFLOATS for that reason and maximize with 0 at the
-				//  very last point - before returning desired size to the parent.
-				clippedDesiredWidth = desiredSize.Width + roundedMarginWidth;
-				clippedDesiredHeight = desiredSize.Height + roundedMarginHeight;
-
-				// only clip and constrain if the tree wants that.
-				// currently only listviewitems do not want clipping
-				// UNO TODO
-
-				//if (!pLayoutManager->GetIsInNonClippingTree())
-				{
-					// In overconstrained scenario, parent wins and measured size of the child,
-					// including any sizes set or computed, can not be larger then
-					// available size. We will clip the guy later.
-					if (clippedDesiredWidth > availableSize.Width)
-					{
-						clippedDesiredWidth = availableSize.Width;
-					}
-
-					if (clippedDesiredHeight > availableSize.Height)
-					{
-						clippedDesiredHeight = availableSize.Height;
-					}
-				}
-
-				//  Note: unclippedDesiredSize is needed in ArrangeCore,
-				//  because due to the layout protocol, arrange should be called
-				//  with constraints greater or equal to child's desired size
-				//  returned from MeasureOverride. But in most circumstances
-				//  it is possible to reconstruct original unclipped desired size.
-
-				desiredSize.Width = Math.Max(0, clippedDesiredWidth);
-				desiredSize.Height = Math.Max(0, clippedDesiredHeight);
-			}
-			//else
-			//{
-			//	// in LT, need to take precautions
-			//	desiredSize.Width = Math.Max(desiredSize.Width, 0.0f);
-			//	desiredSize.Height = Math.Max(desiredSize.Height, 0.0f);
-			//}
-
-			// We need to round again in case the desired size has been modified since we originally
-			// rounded it.
-			if (GetUseLayoutRounding())
-			{
-				desiredSize.Width = LayoutRound(desiredSize.Width);
-				desiredSize.Height = LayoutRound(desiredSize.Height);
-			}
-
-			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
-			{
-				this.Log().LogWarning($"[LayoutCycleTracing] Measured {this},{this.GetDebugName()}: desiredSize is {desiredSize}.");
-			}
-
-#if __SKIA__
-			if (desiredSize != DesiredSize)
-#endif
-			{
-				// DesiredSize must include margins
-				m_desiredSize = desiredSize;
-#if __SKIA__
-				this.OnDesiredSizeChanged();
-#endif
-			}
-
-			_logDebug?.Debug($"{DepthIndentation}[{FormatDebugName()}] Measure({Name}/{availableSize}/{Margin}) = {desiredSize} _unclippedDesiredSize={m_unclippedDesiredSize}");
-		}
-
-		private protected virtual void OnDesiredSizeChanged()
-		{
-
-		}
-
 		private void RaiseLoadingEventIfNeeded()
 		{
 			if (!m_firedLoadingEvent //&&
@@ -400,519 +149,6 @@ private void RaiseLoadingEventIfNeeded()
 			}
 		}
 
-		private string FormatDebugName()
-			=> $"[{this}/{Name}";
-
-		internal sealed override void ArrangeCore(Rect finalRect)
-		{
-			if (_trace.IsEnabled)
-			{
-				void ArrangeCoreWithTrace(Rect finalRect)
-				{
-					var traceActivity = _trace.WriteEventActivity(
-										TraceProvider.FrameworkElement_ArrangeStart,
-										TraceProvider.FrameworkElement_ArrangeStop,
-										new object[] { GetType().Name, this.GetDependencyObjectId(), Name, finalRect.ToString() }
-									);
-
-					using (traceActivity)
-					{
-						InnerArrangeCore(finalRect);
-					}
-				}
-
-				ArrangeCoreWithTrace(finalRect);
-			}
-			else
-			{
-				// This method is split in two functions to avoid the dynCalls
-				// invocations generation for mono-wasm AOT inside of try/catch/finally blocks.
-				InnerArrangeCore(finalRect);
-			}
-
-		}
-
-		private static bool IsLessThanAndNotCloseTo(double a, double b) => a < (b - SIZE_EPSILON);
-
-		private void InnerArrangeCore(Rect finalRect)
-		{
-			_logDebug?.Debug($"{DepthIndentation}{FormatDebugName()}: InnerArrangeCore({finalRect})");
-			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
-			{
-				this.Log().LogWarning($"[LayoutCycleTracing] Arranging {this},{this.GetDebugName()} with finalRect {finalRect}.");
-			}
-
-			// Uno TODO:
-			//CLayoutManager* pLayoutManager = VisualTree::GetLayoutManagerForElement(this);
-			//bool bInLayoutTransition = pLayoutManager ? pLayoutManager->GetTransitioningElement() == this : false;
-
-			bool needsClipBounds = false;
-
-			var arrangeSize = finalRect.Size;
-
-			var margin = Margin;
-			var marginWidth = margin.Left + margin.Right;
-			var marginHeight = margin.Top + margin.Bottom;
-
-			var ha = HorizontalAlignment;
-			var va = VerticalAlignment;
-
-			Size unclippedDesiredSize = default;
-			double minWidth = 0, maxWidth = 0, minHeight = 0, maxHeight = 0;
-			double effectiveMaxWidth = 0, effectiveMaxHeight = 0;
-
-			Size oldRenderSize = default;
-			Size innerInkSize = default;
-			Size clippedInkSize = default;
-			Size clientSize = default;
-			double offsetX = 0, offsetY = 0;
-
-			EnsureLayoutStorage();
-
-			unclippedDesiredSize = m_unclippedDesiredSize;
-			oldRenderSize = RenderSize;
-
-			//if (!bInLayoutTransition)
-			{
-				Size arrangeSizeWithoutMargin = new Size(
-					Math.Max(arrangeSize.Width - marginWidth, 0),
-					Math.Max(arrangeSize.Height - marginHeight, 0)
-				);
-
-				var roundedMarginWidth = marginWidth;
-				var roundedMarginHeight = marginHeight;
-
-				if (GetUseLayoutRounding())
-				{
-					roundedMarginWidth = LayoutRound(marginWidth);
-					roundedMarginHeight = LayoutRound(marginHeight);
-				}
-
-				// We handle layout rounding inconsistently across our code, this change is restricted to using layout
-				// rounding for margin only on certain scenarios to avoid introducing unexpected problems.
-				// If further rounding issues appear we should consider opening this behaviour to more scenarios.
-				if (roundedMarginWidth != marginWidth && arrangeSizeWithoutMargin.Width != unclippedDesiredSize.Width)
-				{
-					double arrangeWidthWithoutRoundedMargin = Math.Max(arrangeSize.Width - roundedMarginWidth, 0);
-					if (arrangeWidthWithoutRoundedMargin == unclippedDesiredSize.Width)
-					{
-						// The rounding difference between arrangeSizeWithoutMargin.width and unclippedDesiredSize.width
-						// comes from the horizontal margin. The rounded value of that margin must be used so that this
-						// FrameworkElement's ActualWidth does not return an incorrect value.
-						marginWidth = roundedMarginWidth;
-						arrangeSize.Width = arrangeWidthWithoutRoundedMargin;
-					}
-					else
-					{
-						arrangeSize.Width = arrangeSizeWithoutMargin.Width;
-					}
-				}
-				else
-				{
-					arrangeSize.Width = arrangeSizeWithoutMargin.Width;
-				}
-
-				if (roundedMarginHeight != marginHeight && arrangeSizeWithoutMargin.Height != unclippedDesiredSize.Height)
-				{
-					double arrangeHeightWithoutRoundedMargin = Math.Max(arrangeSize.Height - roundedMarginHeight, 0);
-					if (arrangeHeightWithoutRoundedMargin == unclippedDesiredSize.Height)
-					{
-						// The rounding difference between arrangeSizeWithoutMargin.height and unclippedDesiredSize.height
-						// comes from the vertical margin. The rounded value of that margin must be used so that this
-						// FrameworkElement's ActualHeight does not return an incorrect value.
-						marginHeight = roundedMarginHeight;
-						arrangeSize.Height = arrangeHeightWithoutRoundedMargin;
-					}
-					else
-					{
-						arrangeSize.Height = arrangeSizeWithoutMargin.Height;
-					}
-				}
-				else
-				{
-					arrangeSize.Height = arrangeSizeWithoutMargin.Height;
-				}
-
-				if (IsLessThanAndNotCloseTo(arrangeSize.Width, unclippedDesiredSize.Width))
-				{
-					needsClipBounds = true;
-					arrangeSize.Width = unclippedDesiredSize.Width;
-				}
-
-				if (IsLessThanAndNotCloseTo(arrangeSize.Height, unclippedDesiredSize.Height))
-				{
-					needsClipBounds = true;
-					arrangeSize.Height = unclippedDesiredSize.Height;
-				}
-
-				// Alignment==Stretch --> arrange at the slot size minus margins
-				// Alignment!=Stretch --> arrange at the unclippedDesiredSize
-				if (ha != HorizontalAlignment.Stretch)
-				{
-					arrangeSize.Width = unclippedDesiredSize.Width;
-				}
-
-				if (va != VerticalAlignment.Stretch)
-				{
-					arrangeSize.Height = unclippedDesiredSize.Height;
-				}
-
-				var (minSize, maxSize) = this.GetMinMax();
-				minWidth = minSize.Width;
-				maxWidth = maxSize.Width;
-				minHeight = minSize.Height;
-				maxHeight = maxSize.Height;
-
-				// Layout transforms processed here
-
-				// We have to choose max between UnclippedDesiredSize and Max here, because
-				// otherwise setting of max property could cause arrange at less then unclippedDS.
-				// Clipping by Max is needed to limit stretch here
-
-				effectiveMaxWidth = Math.Max(unclippedDesiredSize.Width, maxWidth);
-				if (IsLessThanAndNotCloseTo(effectiveMaxWidth, arrangeSize.Width))
-				{
-					needsClipBounds = true;
-					arrangeSize.Width = effectiveMaxWidth;
-				}
-
-				effectiveMaxHeight = Math.Max(unclippedDesiredSize.Height, maxHeight);
-				if (IsLessThanAndNotCloseTo(effectiveMaxHeight, arrangeSize.Height))
-				{
-					needsClipBounds = true;
-					arrangeSize.Height = effectiveMaxHeight;
-				}
-			}
-
-			innerInkSize = ArrangeOverride(arrangeSize);
-
-			// Here we use un-clipped InkSize because element does not know that it is
-			// clipped by layout system and it shoudl have as much space to render as
-			// it returned from its own ArrangeOverride
-			// Inner ink size is not guaranteed to be rounded, but should be.
-			// TODO: inner ink size currently only rounded if plateau > 1 to minimize impact in RC,
-			// but should be consistently rounded in all plateaus.
-			var scale = RootScale.GetRasterizationScaleForElement(this);
-			if ((scale != 1.0f) && GetUseLayoutRounding())
-			{
-				innerInkSize.Width = LayoutRound(innerInkSize.Width);
-				innerInkSize.Height = LayoutRound(innerInkSize.Height);
-			}
-			RenderSize = innerInkSize;
-
-			//if (!IsSameSize(oldRenderSize, innerInkSize))
-			//{
-			//	OnActualSizeChanged();
-			//}
-
-			if (oldRenderSize != innerInkSize)
-			{
-				this.GetContext().EventManager.EnqueueForSizeChanged(this, oldRenderSize);
-			}
-
-			//if (!bInLayoutTransition)
-			{
-				// ClippedInkSize differs from InkSize only what MaxWidth/Height explicitly clip the
-				// otherwise good arrangement. For ex, DS<clientSize but DS>MaxWidth - in this
-				// case we should initiate clip at MaxWidth and only show Top-Left portion
-				// of the element limited by Max properties. It is Top-left because in case when we
-				// are clipped by container we also degrade to Top-Left, so we are consistent.
-				clippedInkSize.Width = Math.Min(innerInkSize.Width, maxWidth);
-				clippedInkSize.Height = Math.Min(innerInkSize.Height, maxHeight);
-
-				// remember we have to clip if Max properties limit the inkSize
-				needsClipBounds |=
-					IsLessThanAndNotCloseTo(clippedInkSize.Width, innerInkSize.Width)
-					|| IsLessThanAndNotCloseTo(clippedInkSize.Height, innerInkSize.Height);
-
-				// Transform stuff here
-
-				// Note that inkSize now can be bigger then layoutSlotSize-margin (because of layout
-				// squeeze by the parent or LayoutConstrained=true, which clips desired size in Measure).
-
-				// The client size is the size of layout slot decreased by margins.
-				// This is the "window" through which we see the content of the child.
-				// Alignments position ink of the child in this "window".
-				// Max with 0 is necessary because layout slot may be smaller then unclipped desired size.
-				clientSize.Width = Math.Max(0, finalRect.Width - marginWidth);
-				clientSize.Height = Math.Max(0, finalRect.Height - marginHeight);
-
-				// Remember we have to clip if clientSize limits the inkSize
-				needsClipBounds |=
-					IsLessThanAndNotCloseTo(clientSize.Width, clippedInkSize.Width)
-					|| IsLessThanAndNotCloseTo(clientSize.Height, clippedInkSize.Height);
-
-				//bool isAlignedByDirectManipulation = IsAlignedByDirectManipulation();
-
-				//if (isAlignedByDirectManipulation)
-				//{
-				//	// Skip the layout engine's contribution to the element's offsets when it is already aligned by DirectManipulation.
-
-				//	if (m_pLayoutProperties.m_horizontalAlignment == HorizontalAlignment.Stretch)
-				//	{
-				//		// Check if the Stretch alignment needs to be overridden with a Left alignment.
-				//		// The "IsStretchHorizontalAlignmentTreatedAsLeft" case corresponds to CFrameworkElement::ComputeAlignmentOffset's "degenerate Stretch to Top-Left" branch.
-				//		// The "IsFinalArrangeSizeMaximized()" case is for text controls CTextBlock, CRichTextBlock and CRichTextBlockOverflow which stretch their desired width to the finalSize argument in their ArrangeOverride method.
-				//		// The "(clippedInkSize.width == clientSize.width && unclippedDesiredSize.width < clientSize.width)" case is for 3rd party controls that stretch their desired width to the final arrange width too.
-				//		bool isStretchAlignmentTreatedAsNear_New =
-				//			IsStretchHorizontalAlignmentTreatedAsLeft(HorizontalAlignment.Stretch, clientSize, clippedInkSize) ||
-				//			(clippedInkSize.Width == clientSize.Width && unclippedDesiredSize.Width < clientSize.Width) ||
-				//			IsFinalArrangeSizeMaximized();
-
-				//		// Check if the overriding needs are changing by accessing the current status from the owning ScrollViewer control.
-				//		bool isStretchAlignmentTreatedAsNear_Old = IsStretchAlignmentTreatedAsNear(true /*isForHorizontalAlignment*/);
-				//		if (isStretchAlignmentTreatedAsNear_New != isStretchAlignmentTreatedAsNear_Old)
-				//		{
-				//			// The overriding needs are changing - push the new status to the owning ScrollViewer control.
-				//			OnAlignmentChanged(true /*fIsForHorizontalAlignment*/, true/*fIsForStretchAlignment*/, isStretchAlignmentTreatedAsNear_New);
-				//		}
-				//	}
-
-				//	if (m_pLayoutProperties.m_verticalAlignment == VerticalAlignment.Stretch)
-				//	{
-				//		// Check if the Stretch alignment needs to be overridden with a Top alignment.
-				//		// The "IsStretchVerticalAlignmentTreatedAsTop" case corresponds to CFrameworkElement::ComputeAlignmentOffset's "degenerate Stretch to Top-Left" branch.
-				//		// The "IsFinalArrangeSizeMaximized()" case is for text controls CTextBlock, CRichTextBlock and CRichTextBlockOverflow which stretch their desired height to the finalSize argument in their ArrangeOverride method.
-				//		// The "(clippedInkSize.height == clientSize.height && unclippedDesiredSize.height < clientSize.height)" case is for 3rd party controls that stretch their desired height to the final arrange height too.
-				//		bool isStretchAlignmentTreatedAsNear_New =
-				//			IsStretchVerticalAlignmentTreatedAsTop(VerticalAlignment.Stretch, clientSize, clippedInkSize) ||
-				//			(clippedInkSize.Height == clientSize.Height && unclippedDesiredSize.Height < clientSize.Height) ||
-				//			IsFinalArrangeSizeMaximized();
-
-				//		// Check if the overriding needs are changing by accessing the current status from the owning ScrollViewer control.
-				//		bool isStretchAlignmentTreatedAsNear_Old = IsStretchAlignmentTreatedAsNear(false /*isForHorizontalAlignment*/);
-				//		if (isStretchAlignmentTreatedAsNear_New != isStretchAlignmentTreatedAsNear_Old)
-				//		{
-				//			// The overriding needs are changing - push the new status to the owning ScrollViewer control.
-				//			OnAlignmentChanged(false /*fIsForHorizontalAlignment*/, true /*fIsForStretchAlignment*/, isStretchAlignmentTreatedAsNear_New);
-				//		}
-				//	}
-				//}
-				//else
-				{
-					var offset = this.GetAlignmentOffset(clientSize, clippedInkSize);
-					offsetX = offset.X;
-					offsetY = offset.Y;
-				}
-
-				//oldOffset = VisualOffset;
-
-				//VisualOffset.x = offsetX + finalRect.X + m_pLayoutProperties->m_margin.left;
-				//VisualOffset.y = offsetY + finalRect.Y + m_pLayoutProperties->m_margin.top;
-
-				offsetX = offsetX + finalRect.X + margin.Left;
-				offsetY = offsetY + finalRect.Y + margin.Top;
-
-				if (GetUseLayoutRounding())
-				{
-					offsetX = LayoutRound(offsetX);
-					offsetY = LayoutRound(offsetY);
-				}
-			}
-			//else
-			//{
-			//	offsetX = finalRect.X;
-			//	offsetY = finalRect.Y;
-			//}
-
-			NeedsClipToSlot = needsClipBounds;
-
-#if __WASM__
-			if (FeatureConfiguration.UIElement.AssignDOMXamlProperties)
-			{
-				UpdateDOMXamlProperty(nameof(NeedsClipToSlot), NeedsClipToSlot);
-			}
-#endif
-			var visualOffset = new Point(offsetX, offsetY);
-			var clippedFrame = GetClipRect(needsClipBounds, visualOffset, finalRect, new Size(maxWidth, maxHeight), margin);
-			ArrangeNative(visualOffset, clippedFrame);
-
-			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
-			{
-				this.Log().LogWarning($"[LayoutCycleTracing] Arranged {this},{this.GetDebugName()}: {clippedFrame}.");
-			}
-
-			AfterArrange();
-		}
-
-		internal virtual void AfterArrange() { }
-
-		// Part of this code originates from https://github.com/dotnet/wpf/blob/b9b48871d457fc1f78fa9526c0570dae8e34b488/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/FrameworkElement.cs#L4877
-		private protected virtual Rect? GetClipRect(bool needsClipToSlot, Point visualOffset, Rect finalRect, Size maxSize, Thickness margin)
-		{
-			if (needsClipToSlot)
-			{
-				Rect clipRect = default;
-
-				// TODO: Clip rect currently only rounded in plateau > 1 to minimize impact, but should be consistently rounded in all plateaus.
-				var scale = RootScale.GetRasterizationScaleForElement(this);
-				var roundClipRect = (scale != 1.0f) && GetUseLayoutRounding();
-
-				var maxWidth = maxSize.Width;
-				var maxHeight = maxSize.Height;
-
-				// this is in element's local rendering coord system
-				var inkSize = RenderSize;
-				var layoutSlotSize = finalRect.Size;
-
-				var maxWidthClip = maxSize.Width.FiniteOrDefault(inkSize.Width);
-				var maxHeightClip = maxSize.Height.FiniteOrDefault(inkSize.Height);
-
-				bool needToClipLocally;
-
-				Size clippingSize = default;
-
-				EnsureLayoutStorage();
-
-				// If clipping is forced, ensure the clip is at least as small as the RenderSize.
-				//if (forceClipToRenderSize)
-				//{
-				//	maxWidthClip = MIN(inkSize.width, maxWidthClip);
-				//	maxHeightClip = MIN(inkSize.height, maxHeightClip);
-				//	needToClipLocally = TRUE;
-				//}
-				//else
-				{
-					// need to clip if the computed sizes exceed MaxWidth/MaxHeight/Width/Height
-					needToClipLocally = IsLessThanAndNotCloseTo(maxWidthClip, inkSize.Width)
-									 || IsLessThanAndNotCloseTo(maxHeightClip, inkSize.Height);
-				}
-
-				// now lets say we already clipped by MaxWidth/MaxHeight, lets see if further clipping is needed
-				inkSize.Width = Math.Min(inkSize.Width, maxWidth);
-				inkSize.Height = Math.Min(inkSize.Height, maxHeight);
-
-				// now lets say we already clipped by MaxWidth/MaxHeight, lets see if further clipping is needed
-				inkSize.Width = Math.Min(inkSize.Width, maxWidth);
-				inkSize.Height = Math.Min(inkSize.Height, maxHeight);
-
-				//now see if layout slot should clip the element
-				var marginWidth = margin.Left + margin.Right;
-				var marginHeight = margin.Top + margin.Bottom;
-
-				clippingSize.Width = Math.Max(0, layoutSlotSize.Width - marginWidth);
-				clippingSize.Height = Math.Max(0, layoutSlotSize.Height - marginHeight);
-
-				// With layout rounding, MinMax and RenderSize are rounded. Clip size should be rounded as well.
-				if (roundClipRect)
-				{
-					clippingSize.Width = LayoutRound(clippingSize.Width);
-					clippingSize.Height = LayoutRound(clippingSize.Height);
-				}
-
-				bool needToClipSlot = IsLessThanAndNotCloseTo(clippingSize.Width, inkSize.Width)
-					|| IsLessThanAndNotCloseTo(clippingSize.Height, inkSize.Height);
-
-
-				if (needToClipSlot)
-				{
-					// The layout clip is created from the slot size determined in the parent's coordinate space,
-					// but is set on the child, meaning it's affected by the child's transform/offset and is applied in
-					// the child's coordinate space.  The inverse of the offset is applied to the clip to prevent the clip's
-					// position from shifting as a result of the change in coordinates.
-					var offset = LayoutHelper.GetAlignmentOffset(this, clippingSize, inkSize);
-					var offsetX = offset.X;
-					var offsetY = offset.Y;
-					if (roundClipRect)
-					{
-						offsetX = LayoutRound(offsetX);
-						offsetY = LayoutRound(offsetY);
-					}
-
-					clipRect = new Rect(-offsetX, -offsetY, clippingSize.Width, clippingSize.Height);
-					if (needToClipLocally)
-					{
-						clipRect = clipRect.IntersectWith(new Rect(0, 0, maxWidthClip, maxHeightClip)) ?? Rect.Empty;
-					}
-				}
-				else if (needToClipLocally)
-				{
-					// In this case clipRect starts at 0, 0 and max width/height clips are rounded due
-					// to RenderSize and MinMax being rounded. So clipRect is already rounded.
-					clipRect.Width = maxWidthClip;
-					clipRect.Height = maxHeightClip;
-				}
-
-				// if we have difference between child and parent FlowDirection
-				// then we have to change origin of Clipping rectangle
-				// which allows us to visually keep it at the same place
-				// UNO TODO
-				//pParent = GetUIElementAdjustedParentInternal(FALSE /*fPublicParentsOnly*/);
-				//if (pParent && pParent->IsRightToLeft() != IsRightToLeft())
-				//{
-				//	clipRect.X = RenderSize.width - (clipRect.X + clipRect.Width);
-				//}
-
-				if (needToClipSlot || needToClipLocally)
-				{
-					if (ShouldApplyLayoutClipAsAncestorClip()
-#if __WASM__
-						&& RenderTransform is { } renderTransform
-#endif
-						)
-					{
-#if __SKIA__
-						clipRect.X += visualOffset.X;
-						clipRect.Y += visualOffset.Y;
-#elif __WASM__
-						clipRect.X -= renderTransform.MatrixCore.M31;
-						clipRect.Y -= renderTransform.MatrixCore.M32;
-#endif
-					}
-
-					return clipRect;
-				}
-			}
-
-			return null;
-		}
-
-		/// <summary>
-		/// Calculates and applies native arrange properties.
-		/// </summary>
-		/// <param name="offset">Offset of the view from its parent</param>
-		/// <param name="clippedFrame">Zone to clip, if clipping is required</param>
-		private void ArrangeNative(Point offset, Rect? clippedFrame)
-		{
-			var newRect = new Rect(offset, RenderSize);
-
-			if (
-				newRect.Width < 0
-				|| newRect.Height < 0
-				|| double.IsNaN(newRect.Width)
-				|| double.IsNaN(newRect.Height)
-				|| double.IsNaN(newRect.X)
-				|| double.IsNaN(newRect.Y)
-			)
-			{
-				throw new InvalidOperationException($"{FormatDebugName()}: Invalid frame size {newRect}. No dimension should be NaN or negative value.");
-			}
-
-#if __SKIA__
-			// clippedFrame here is the one calculated by FrameworkElement.GetClipRect
-			// which propagates to ShapeVisual.ViewBox.
-			// The UIElement.Clip public property isn't considered here on Skia because
-			// it's propagated to Visual.Clip and is set when UIElement.Clip changes.
-			ArrangeVisual(newRect, clippedFrame);
-#else
-			var clip = Clip;
-			var clipRect = clip?.Rect;
-			if (clipRect.HasValue && clip?.Transform is { } transform)
-			{
-				clipRect = transform.TransformBounds(clipRect.Value);
-			}
-
-			if (clipRect.HasValue || clippedFrame.HasValue)
-			{
-				clipRect = (clipRect ?? Rect.Infinite).IntersectWith(clippedFrame ?? Rect.Infinite);
-			}
-
-			_logDebug?.Trace($"{DepthIndentation}{FormatDebugName()}.ArrangeElementNative({newRect}, clip={clipRect} (NeedsClipToSlot={NeedsClipToSlot})");
-
-			ArrangeVisual(newRect, clipRect);
-#endif
-		}
-
 		internal override void EnterImpl(EnterParams @params, int depth)
 		{
 			var core = this.GetContext();
diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.cs
new file mode 100644
index 000000000000..281edf02472b
--- /dev/null
+++ b/src/Uno.UI/UI/Xaml/FrameworkElement.Layout.cs
@@ -0,0 +1,790 @@
+#if !__NETSTD_REFERENCE__
+#nullable enable
+using System;
+using System.Globalization;
+using System.Linq;
+using Uno.Diagnostics.Eventing;
+using Uno.Extensions;
+using Uno.Foundation.Logging;
+using Windows.Foundation;
+using Microsoft.UI.Xaml.Controls.Primitives;
+
+using Uno.UI;
+using Uno.UI.Xaml;
+using static System.Math;
+using static Uno.UI.LayoutHelper;
+using Microsoft.UI.Xaml.Controls;
+using Uno.UI.Xaml.Core;
+using Uno.UI.Xaml.Core.Scaling;
+using Uno.UI.Extensions;
+
+namespace Microsoft.UI.Xaml
+{
+	public partial class FrameworkElement
+	{
+		private const double SIZE_EPSILON = 0.05d;
+		private readonly static IEventProvider _trace = Tracing.Get(FrameworkElement.TraceProvider.Id);
+
+		internal sealed override void MeasureCore(Size availableSize)
+		{
+			if (_trace.IsEnabled)
+			{
+				/// <remarks>
+				/// This method contains or is called by a try/catch containing method and
+				/// can be significantly slower than other methods as a result on WebAssembly.
+				/// See https://github.com/dotnet/runtime/issues/56309
+				/// </remarks>
+				void MeasureCoreWithTrace(Size availableSize)
+				{
+					var traceActivity = _trace.WriteEventActivity(
+										TraceProvider.FrameworkElement_MeasureStart,
+										TraceProvider.FrameworkElement_MeasureStop,
+										new object[] { GetType().Name, this.GetDependencyObjectId(), Name, availableSize.ToString() }
+									);
+
+					using (traceActivity)
+					{
+						InnerMeasureCore(availableSize);
+					}
+				}
+
+				MeasureCoreWithTrace(availableSize);
+			}
+			else
+			{
+				// This method is split in two functions to avoid the dynCalls
+				// invocations generation for mono-wasm AOT inside of try/catch/finally blocks.
+				InnerMeasureCore(availableSize);
+			}
+
+		}
+
+		private void InnerMeasureCore(Size availableSize)
+		{
+			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
+			{
+				this.Log().LogWarning($"[LayoutCycleTracing] Measuring {this},{this.GetDebugName()} with availableSize {availableSize}.");
+			}
+
+			// Uno TODO
+			//CLayoutManager* pLayoutManager = VisualTree::GetLayoutManagerForElement(this);
+			//bool bInLayoutTransition = pLayoutManager ? pLayoutManager->GetTransitioningElement() == this : false;
+
+			Size frameworkAvailableSize = default;
+			double minWidth = 0.0f;
+			double maxWidth = 0.0f;
+			double minHeight = 0.0f;
+			double maxHeight = 0.0f;
+
+			double clippedDesiredWidth;
+			double clippedDesiredHeight;
+
+			double marginWidth = 0.0f;
+			double marginHeight = 0.0f;
+
+			//bool bTemplateApplied = false;
+
+#if UNO_HAS_ENHANCED_LIFECYCLE
+			RaiseLoadingEventIfNeeded();
+#endif
+
+			//if (!bInLayoutTransition)
+			{
+				// Templates should be applied here.
+				//bTemplateApplied = InvokeApplyTemplate();
+
+				// TODO: BEGIN Uno specific
+				if (this is Control thisAsControl)
+				{
+					thisAsControl.ApplyTemplate();
+
+					// Update bindings to ensure resources defined
+					// in visual parents get applied.
+					this.UpdateResourceBindings();
+				}
+				// TODO: END Uno specific
+
+				// Subtract the margins from the available size
+				var margin = Margin;
+				marginWidth = margin.Left + margin.Right;
+				marginHeight = margin.Top + margin.Bottom;
+
+				// We check to see if availableSize.width and availableSize.height are finite since that will
+				// also protect against NaN getting in.
+				frameworkAvailableSize.Width = double.IsFinite(availableSize.Width) ? Math.Max(availableSize.Width - marginWidth, 0) : double.PositiveInfinity;
+				frameworkAvailableSize.Height = double.IsFinite(availableSize.Height) ? Math.Max(availableSize.Height - marginHeight, 0) : double.PositiveInfinity;
+
+				// Layout transforms would get processed here.
+
+				// Adjust available size by Min/Max Width/Height
+
+				var (minSize, maxSize) = this.GetMinMax();
+				minWidth = minSize.Width;
+				minHeight = minSize.Height;
+				maxWidth = maxSize.Width;
+				maxHeight = maxSize.Height;
+
+				frameworkAvailableSize.Width = Math.Max(minWidth, Math.Min(frameworkAvailableSize.Width, maxWidth));
+				frameworkAvailableSize.Height = Math.Max(minHeight, Math.Min(frameworkAvailableSize.Height, maxHeight));
+			}
+			//else
+			//{
+			//	// when in a transition, just take the passed in constraint without considering the above
+			//	frameworkAvailableSize = availableSize;
+			//}
+
+			var desiredSize = MeasureOverride(frameworkAvailableSize);
+
+			// We need to round now since we save the values off, and use them to determine
+			// if a layout clip will be applied.
+			if (GetUseLayoutRounding())
+			{
+				desiredSize.Width = LayoutRound(desiredSize.Width);
+				desiredSize.Height = LayoutRound(desiredSize.Height);
+
+			}
+
+			//if (!bInLayoutTransition)
+			{
+				// Maximize desired size with user provided min size. It's also possible that MeasureOverride returned NaN for either
+				// width or height, in which case we should use the min size as well.
+				desiredSize.Width = Math.Max(desiredSize.Width, minWidth);
+				if (double.IsNaN(desiredSize.Width))
+				{
+					desiredSize.Width = minWidth;
+				}
+				desiredSize.Height = Math.Max(desiredSize.Height, minHeight);
+				if (double.IsNaN(desiredSize.Height))
+				{
+					desiredSize.Height = minHeight;
+				}
+
+				// We need to round now since we save the values off, and use them to determine
+				// if a layout clip will be applied.
+
+				if (GetUseLayoutRounding())
+				{
+					desiredSize.Width = LayoutRound(desiredSize.Width);
+					desiredSize.Height = LayoutRound(desiredSize.Height);
+				}
+
+				// Here is the "true minimum" desired size - the one that is
+				// for sure enough for the control to render its content.
+				EnsureLayoutStorage();
+				m_unclippedDesiredSize = desiredSize;
+
+				// More layout transforms processing here.
+
+				if (desiredSize.Width > maxWidth)
+				{
+					desiredSize.Width = maxWidth;
+				}
+
+				if (desiredSize.Height > maxHeight)
+				{
+					desiredSize.Height = maxHeight;
+				}
+
+				// Transform desired size to layout slot space (placeholder for when we do layout transforms)
+
+				// Layout round the margins too. This corresponds to the behavior in ArrangeCore, where we check the unclipped desired
+				// size against available space minus the rounded margin. This also prevents a bug where MeasureCore adds the unrounded
+				// margin (e.g. 14) to the desired size (e.g. 55.56) and rounds the final result (69.56 rounded to 69.33 under 2.25x scale),
+				// then ArrangeCore takes that rounded result (i.e. 69.33), subtracts the unrounded margin (i.e. 14) and ends up with a
+				// size smaller than the desired size (69.33 - 14 = 55.33 < 55.56). This ends up putting a layout clip on an element that
+				// doesn't need one, and causes big problems if the element is the scrollable extent of a carousel panel.
+				double roundedMarginWidth = marginWidth;
+				double roundedMarginHeight = marginHeight;
+				if (GetUseLayoutRounding())
+				{
+					roundedMarginWidth = LayoutRound(marginWidth);
+					roundedMarginHeight = LayoutRound(marginHeight);
+				}
+
+				//  Because of negative margins, clipped desired size may be negative.
+				//  Need to keep it as XFLOATS for that reason and maximize with 0 at the
+				//  very last point - before returning desired size to the parent.
+				clippedDesiredWidth = desiredSize.Width + roundedMarginWidth;
+				clippedDesiredHeight = desiredSize.Height + roundedMarginHeight;
+
+				// only clip and constrain if the tree wants that.
+				// currently only listviewitems do not want clipping
+				// UNO TODO
+
+				//if (!pLayoutManager->GetIsInNonClippingTree())
+				{
+					// In overconstrained scenario, parent wins and measured size of the child,
+					// including any sizes set or computed, can not be larger then
+					// available size. We will clip the guy later.
+					if (clippedDesiredWidth > availableSize.Width)
+					{
+						clippedDesiredWidth = availableSize.Width;
+					}
+
+					if (clippedDesiredHeight > availableSize.Height)
+					{
+						clippedDesiredHeight = availableSize.Height;
+					}
+				}
+
+				//  Note: unclippedDesiredSize is needed in ArrangeCore,
+				//  because due to the layout protocol, arrange should be called
+				//  with constraints greater or equal to child's desired size
+				//  returned from MeasureOverride. But in most circumstances
+				//  it is possible to reconstruct original unclipped desired size.
+
+				desiredSize.Width = Math.Max(0, clippedDesiredWidth);
+				desiredSize.Height = Math.Max(0, clippedDesiredHeight);
+			}
+			//else
+			//{
+			//	// in LT, need to take precautions
+			//	desiredSize.Width = Math.Max(desiredSize.Width, 0.0f);
+			//	desiredSize.Height = Math.Max(desiredSize.Height, 0.0f);
+			//}
+
+			// We need to round again in case the desired size has been modified since we originally
+			// rounded it.
+			if (GetUseLayoutRounding())
+			{
+				desiredSize.Width = LayoutRound(desiredSize.Width);
+				desiredSize.Height = LayoutRound(desiredSize.Height);
+			}
+
+			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
+			{
+				this.Log().LogWarning($"[LayoutCycleTracing] Measured {this},{this.GetDebugName()}: desiredSize is {desiredSize}.");
+			}
+
+#if __SKIA__
+			if (desiredSize != DesiredSize)
+#endif
+			{
+				// DesiredSize must include margins
+				m_desiredSize = desiredSize;
+
+#if __SKIA__
+				this.OnDesiredSizeChanged();
+#endif
+			}
+		}
+
+		private protected virtual void OnDesiredSizeChanged()
+		{
+
+		}
+
+		internal sealed override void ArrangeCore(Rect finalRect)
+		{
+			if (_trace.IsEnabled)
+			{
+				void ArrangeCoreWithTrace(Rect finalRect)
+				{
+					var traceActivity = _trace.WriteEventActivity(
+										TraceProvider.FrameworkElement_ArrangeStart,
+										TraceProvider.FrameworkElement_ArrangeStop,
+										new object[] { GetType().Name, this.GetDependencyObjectId(), Name, finalRect.ToString() }
+									);
+
+					using (traceActivity)
+					{
+						InnerArrangeCore(finalRect);
+					}
+				}
+
+				ArrangeCoreWithTrace(finalRect);
+			}
+			else
+			{
+				// This method is split in two functions to avoid the dynCalls
+				// invocations generation for mono-wasm AOT inside of try/catch/finally blocks.
+				InnerArrangeCore(finalRect);
+			}
+
+		}
+
+		private static bool IsLessThanAndNotCloseTo(double a, double b) => a < (b - SIZE_EPSILON);
+
+		private void InnerArrangeCore(Rect finalRect)
+		{
+			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
+			{
+				this.Log().LogWarning($"[LayoutCycleTracing] Arranging {this},{this.GetDebugName()} with finalRect {finalRect}.");
+			}
+
+			// Uno TODO:
+			//CLayoutManager* pLayoutManager = VisualTree::GetLayoutManagerForElement(this);
+			//bool bInLayoutTransition = pLayoutManager ? pLayoutManager->GetTransitioningElement() == this : false;
+
+			bool needsClipBounds = false;
+
+			var arrangeSize = finalRect.Size;
+
+			var margin = Margin;
+			var marginWidth = margin.Left + margin.Right;
+			var marginHeight = margin.Top + margin.Bottom;
+
+			var ha = HorizontalAlignment;
+			var va = VerticalAlignment;
+
+			Size unclippedDesiredSize = default;
+			double minWidth = 0, maxWidth = 0, minHeight = 0, maxHeight = 0;
+			double effectiveMaxWidth = 0, effectiveMaxHeight = 0;
+
+			Size oldRenderSize = default;
+			Size innerInkSize = default;
+			Size clippedInkSize = default;
+			Size clientSize = default;
+			double offsetX = 0, offsetY = 0;
+
+			EnsureLayoutStorage();
+
+			unclippedDesiredSize = m_unclippedDesiredSize;
+			oldRenderSize = RenderSize;
+
+			//if (!bInLayoutTransition)
+			{
+				Size arrangeSizeWithoutMargin = new Size(
+					Math.Max(arrangeSize.Width - marginWidth, 0),
+					Math.Max(arrangeSize.Height - marginHeight, 0)
+				);
+
+				var roundedMarginWidth = marginWidth;
+				var roundedMarginHeight = marginHeight;
+
+				if (GetUseLayoutRounding())
+				{
+					roundedMarginWidth = LayoutRound(marginWidth);
+					roundedMarginHeight = LayoutRound(marginHeight);
+				}
+
+				// We handle layout rounding inconsistently across our code, this change is restricted to using layout
+				// rounding for margin only on certain scenarios to avoid introducing unexpected problems.
+				// If further rounding issues appear we should consider opening this behaviour to more scenarios.
+				if (roundedMarginWidth != marginWidth && arrangeSizeWithoutMargin.Width != unclippedDesiredSize.Width)
+				{
+					double arrangeWidthWithoutRoundedMargin = Math.Max(arrangeSize.Width - roundedMarginWidth, 0);
+					if (arrangeWidthWithoutRoundedMargin == unclippedDesiredSize.Width)
+					{
+						// The rounding difference between arrangeSizeWithoutMargin.width and unclippedDesiredSize.width
+						// comes from the horizontal margin. The rounded value of that margin must be used so that this
+						// FrameworkElement's ActualWidth does not return an incorrect value.
+						marginWidth = roundedMarginWidth;
+						arrangeSize.Width = arrangeWidthWithoutRoundedMargin;
+					}
+					else
+					{
+						arrangeSize.Width = arrangeSizeWithoutMargin.Width;
+					}
+				}
+				else
+				{
+					arrangeSize.Width = arrangeSizeWithoutMargin.Width;
+				}
+
+				if (roundedMarginHeight != marginHeight && arrangeSizeWithoutMargin.Height != unclippedDesiredSize.Height)
+				{
+					double arrangeHeightWithoutRoundedMargin = Math.Max(arrangeSize.Height - roundedMarginHeight, 0);
+					if (arrangeHeightWithoutRoundedMargin == unclippedDesiredSize.Height)
+					{
+						// The rounding difference between arrangeSizeWithoutMargin.height and unclippedDesiredSize.height
+						// comes from the vertical margin. The rounded value of that margin must be used so that this
+						// FrameworkElement's ActualHeight does not return an incorrect value.
+						marginHeight = roundedMarginHeight;
+						arrangeSize.Height = arrangeHeightWithoutRoundedMargin;
+					}
+					else
+					{
+						arrangeSize.Height = arrangeSizeWithoutMargin.Height;
+					}
+				}
+				else
+				{
+					arrangeSize.Height = arrangeSizeWithoutMargin.Height;
+				}
+
+				if (IsLessThanAndNotCloseTo(arrangeSize.Width, unclippedDesiredSize.Width))
+				{
+					needsClipBounds = true;
+					arrangeSize.Width = unclippedDesiredSize.Width;
+				}
+
+				if (IsLessThanAndNotCloseTo(arrangeSize.Height, unclippedDesiredSize.Height))
+				{
+					needsClipBounds = true;
+					arrangeSize.Height = unclippedDesiredSize.Height;
+				}
+
+				// Alignment==Stretch --> arrange at the slot size minus margins
+				// Alignment!=Stretch --> arrange at the unclippedDesiredSize
+				if (ha != HorizontalAlignment.Stretch)
+				{
+					arrangeSize.Width = unclippedDesiredSize.Width;
+				}
+
+				if (va != VerticalAlignment.Stretch)
+				{
+					arrangeSize.Height = unclippedDesiredSize.Height;
+				}
+
+				var (minSize, maxSize) = this.GetMinMax();
+				minWidth = minSize.Width;
+				maxWidth = maxSize.Width;
+				minHeight = minSize.Height;
+				maxHeight = maxSize.Height;
+
+				// Layout transforms processed here
+
+				// We have to choose max between UnclippedDesiredSize and Max here, because
+				// otherwise setting of max property could cause arrange at less then unclippedDS.
+				// Clipping by Max is needed to limit stretch here
+
+				effectiveMaxWidth = Math.Max(unclippedDesiredSize.Width, maxWidth);
+				if (IsLessThanAndNotCloseTo(effectiveMaxWidth, arrangeSize.Width))
+				{
+					needsClipBounds = true;
+					arrangeSize.Width = effectiveMaxWidth;
+				}
+
+				effectiveMaxHeight = Math.Max(unclippedDesiredSize.Height, maxHeight);
+				if (IsLessThanAndNotCloseTo(effectiveMaxHeight, arrangeSize.Height))
+				{
+					needsClipBounds = true;
+					arrangeSize.Height = effectiveMaxHeight;
+				}
+			}
+
+			innerInkSize = ArrangeOverride(arrangeSize);
+
+			// Here we use un-clipped InkSize because element does not know that it is
+			// clipped by layout system and it shoudl have as much space to render as
+			// it returned from its own ArrangeOverride
+			// Inner ink size is not guaranteed to be rounded, but should be.
+			// TODO: inner ink size currently only rounded if plateau > 1 to minimize impact in RC,
+			// but should be consistently rounded in all plateaus.
+			var scale = RootScale.GetRasterizationScaleForElement(this);
+			if ((scale != 1.0f) && GetUseLayoutRounding())
+			{
+				innerInkSize.Width = LayoutRound(innerInkSize.Width);
+				innerInkSize.Height = LayoutRound(innerInkSize.Height);
+			}
+			RenderSize = innerInkSize;
+
+			//if (!IsSameSize(oldRenderSize, innerInkSize))
+			//{
+			//	OnActualSizeChanged();
+			//}
+
+#if UNO_HAS_ENHANCED_LIFECYCLE
+			if (oldRenderSize != innerInkSize)
+			{
+				this.GetContext().EventManager.EnqueueForSizeChanged(this, oldRenderSize);
+			}
+#endif
+
+			//if (!bInLayoutTransition)
+			{
+				// ClippedInkSize differs from InkSize only what MaxWidth/Height explicitly clip the
+				// otherwise good arrangement. For ex, DS<clientSize but DS>MaxWidth - in this
+				// case we should initiate clip at MaxWidth and only show Top-Left portion
+				// of the element limited by Max properties. It is Top-left because in case when we
+				// are clipped by container we also degrade to Top-Left, so we are consistent.
+				clippedInkSize.Width = Math.Min(innerInkSize.Width, maxWidth);
+				clippedInkSize.Height = Math.Min(innerInkSize.Height, maxHeight);
+
+				// remember we have to clip if Max properties limit the inkSize
+				needsClipBounds |=
+					IsLessThanAndNotCloseTo(clippedInkSize.Width, innerInkSize.Width)
+					|| IsLessThanAndNotCloseTo(clippedInkSize.Height, innerInkSize.Height);
+
+				// Transform stuff here
+
+				// Note that inkSize now can be bigger then layoutSlotSize-margin (because of layout
+				// squeeze by the parent or LayoutConstrained=true, which clips desired size in Measure).
+
+				// The client size is the size of layout slot decreased by margins.
+				// This is the "window" through which we see the content of the child.
+				// Alignments position ink of the child in this "window".
+				// Max with 0 is necessary because layout slot may be smaller then unclipped desired size.
+				clientSize.Width = Math.Max(0, finalRect.Width - marginWidth);
+				clientSize.Height = Math.Max(0, finalRect.Height - marginHeight);
+
+				// Remember we have to clip if clientSize limits the inkSize
+				needsClipBounds |=
+					IsLessThanAndNotCloseTo(clientSize.Width, clippedInkSize.Width)
+					|| IsLessThanAndNotCloseTo(clientSize.Height, clippedInkSize.Height);
+
+				//bool isAlignedByDirectManipulation = IsAlignedByDirectManipulation();
+
+				//if (isAlignedByDirectManipulation)
+				//{
+				//	// Skip the layout engine's contribution to the element's offsets when it is already aligned by DirectManipulation.
+
+				//	if (m_pLayoutProperties.m_horizontalAlignment == HorizontalAlignment.Stretch)
+				//	{
+				//		// Check if the Stretch alignment needs to be overridden with a Left alignment.
+				//		// The "IsStretchHorizontalAlignmentTreatedAsLeft" case corresponds to CFrameworkElement::ComputeAlignmentOffset's "degenerate Stretch to Top-Left" branch.
+				//		// The "IsFinalArrangeSizeMaximized()" case is for text controls CTextBlock, CRichTextBlock and CRichTextBlockOverflow which stretch their desired width to the finalSize argument in their ArrangeOverride method.
+				//		// The "(clippedInkSize.width == clientSize.width && unclippedDesiredSize.width < clientSize.width)" case is for 3rd party controls that stretch their desired width to the final arrange width too.
+				//		bool isStretchAlignmentTreatedAsNear_New =
+				//			IsStretchHorizontalAlignmentTreatedAsLeft(HorizontalAlignment.Stretch, clientSize, clippedInkSize) ||
+				//			(clippedInkSize.Width == clientSize.Width && unclippedDesiredSize.Width < clientSize.Width) ||
+				//			IsFinalArrangeSizeMaximized();
+
+				//		// Check if the overriding needs are changing by accessing the current status from the owning ScrollViewer control.
+				//		bool isStretchAlignmentTreatedAsNear_Old = IsStretchAlignmentTreatedAsNear(true /*isForHorizontalAlignment*/);
+				//		if (isStretchAlignmentTreatedAsNear_New != isStretchAlignmentTreatedAsNear_Old)
+				//		{
+				//			// The overriding needs are changing - push the new status to the owning ScrollViewer control.
+				//			OnAlignmentChanged(true /*fIsForHorizontalAlignment*/, true/*fIsForStretchAlignment*/, isStretchAlignmentTreatedAsNear_New);
+				//		}
+				//	}
+
+				//	if (m_pLayoutProperties.m_verticalAlignment == VerticalAlignment.Stretch)
+				//	{
+				//		// Check if the Stretch alignment needs to be overridden with a Top alignment.
+				//		// The "IsStretchVerticalAlignmentTreatedAsTop" case corresponds to CFrameworkElement::ComputeAlignmentOffset's "degenerate Stretch to Top-Left" branch.
+				//		// The "IsFinalArrangeSizeMaximized()" case is for text controls CTextBlock, CRichTextBlock and CRichTextBlockOverflow which stretch their desired height to the finalSize argument in their ArrangeOverride method.
+				//		// The "(clippedInkSize.height == clientSize.height && unclippedDesiredSize.height < clientSize.height)" case is for 3rd party controls that stretch their desired height to the final arrange height too.
+				//		bool isStretchAlignmentTreatedAsNear_New =
+				//			IsStretchVerticalAlignmentTreatedAsTop(VerticalAlignment.Stretch, clientSize, clippedInkSize) ||
+				//			(clippedInkSize.Height == clientSize.Height && unclippedDesiredSize.Height < clientSize.Height) ||
+				//			IsFinalArrangeSizeMaximized();
+
+				//		// Check if the overriding needs are changing by accessing the current status from the owning ScrollViewer control.
+				//		bool isStretchAlignmentTreatedAsNear_Old = IsStretchAlignmentTreatedAsNear(false /*isForHorizontalAlignment*/);
+				//		if (isStretchAlignmentTreatedAsNear_New != isStretchAlignmentTreatedAsNear_Old)
+				//		{
+				//			// The overriding needs are changing - push the new status to the owning ScrollViewer control.
+				//			OnAlignmentChanged(false /*fIsForHorizontalAlignment*/, true /*fIsForStretchAlignment*/, isStretchAlignmentTreatedAsNear_New);
+				//		}
+				//	}
+				//}
+				//else
+				{
+					var offset = this.GetAlignmentOffset(clientSize, clippedInkSize);
+					offsetX = offset.X;
+					offsetY = offset.Y;
+				}
+
+				//oldOffset = VisualOffset;
+
+				//VisualOffset.x = offsetX + finalRect.X + m_pLayoutProperties->m_margin.left;
+				//VisualOffset.y = offsetY + finalRect.Y + m_pLayoutProperties->m_margin.top;
+
+				offsetX = offsetX + finalRect.X + margin.Left;
+				offsetY = offsetY + finalRect.Y + margin.Top;
+
+				if (GetUseLayoutRounding())
+				{
+					offsetX = LayoutRound(offsetX);
+					offsetY = LayoutRound(offsetY);
+				}
+			}
+			//else
+			//{
+			//	offsetX = finalRect.X;
+			//	offsetY = finalRect.Y;
+			//}
+
+			NeedsClipToSlot = needsClipBounds;
+
+#if __WASM__
+			if (FeatureConfiguration.UIElement.AssignDOMXamlProperties)
+			{
+				UpdateDOMXamlProperty(nameof(NeedsClipToSlot), NeedsClipToSlot);
+			}
+#endif
+			var visualOffset = new Point(offsetX, offsetY);
+			var clippedFrame = GetClipRect(needsClipBounds, visualOffset, finalRect, new Size(maxWidth, maxHeight), margin);
+			ArrangeNative(visualOffset, clippedFrame);
+
+			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
+			{
+				this.Log().LogWarning($"[LayoutCycleTracing] Arranged {this},{this.GetDebugName()}: {clippedFrame}.");
+			}
+
+			AfterArrange();
+		}
+
+		internal virtual void AfterArrange() { }
+
+		// Part of this code originates from https://github.com/dotnet/wpf/blob/b9b48871d457fc1f78fa9526c0570dae8e34b488/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/FrameworkElement.cs#L4877
+		private protected virtual Rect? GetClipRect(bool needsClipToSlot, Point visualOffset, Rect finalRect, Size maxSize, Thickness margin)
+		{
+			if (needsClipToSlot)
+			{
+				Rect clipRect = default;
+
+				// TODO: Clip rect currently only rounded in plateau > 1 to minimize impact, but should be consistently rounded in all plateaus.
+				var scale = RootScale.GetRasterizationScaleForElement(this);
+				var roundClipRect = (scale != 1.0f) && GetUseLayoutRounding();
+
+				var maxWidth = maxSize.Width;
+				var maxHeight = maxSize.Height;
+
+				// this is in element's local rendering coord system
+				var inkSize = RenderSize;
+				var layoutSlotSize = finalRect.Size;
+
+				var maxWidthClip = maxSize.Width.FiniteOrDefault(inkSize.Width);
+				var maxHeightClip = maxSize.Height.FiniteOrDefault(inkSize.Height);
+
+				bool needToClipLocally;
+
+				Size clippingSize = default;
+
+				EnsureLayoutStorage();
+
+				// If clipping is forced, ensure the clip is at least as small as the RenderSize.
+				//if (forceClipToRenderSize)
+				//{
+				//	maxWidthClip = MIN(inkSize.width, maxWidthClip);
+				//	maxHeightClip = MIN(inkSize.height, maxHeightClip);
+				//	needToClipLocally = TRUE;
+				//}
+				//else
+				{
+					// need to clip if the computed sizes exceed MaxWidth/MaxHeight/Width/Height
+					needToClipLocally = IsLessThanAndNotCloseTo(maxWidthClip, inkSize.Width)
+									 || IsLessThanAndNotCloseTo(maxHeightClip, inkSize.Height);
+				}
+
+				// now lets say we already clipped by MaxWidth/MaxHeight, lets see if further clipping is needed
+				inkSize.Width = Math.Min(inkSize.Width, maxWidth);
+				inkSize.Height = Math.Min(inkSize.Height, maxHeight);
+
+				// now lets say we already clipped by MaxWidth/MaxHeight, lets see if further clipping is needed
+				inkSize.Width = Math.Min(inkSize.Width, maxWidth);
+				inkSize.Height = Math.Min(inkSize.Height, maxHeight);
+
+				//now see if layout slot should clip the element
+				var marginWidth = margin.Left + margin.Right;
+				var marginHeight = margin.Top + margin.Bottom;
+
+				clippingSize.Width = Math.Max(0, layoutSlotSize.Width - marginWidth);
+				clippingSize.Height = Math.Max(0, layoutSlotSize.Height - marginHeight);
+
+				// With layout rounding, MinMax and RenderSize are rounded. Clip size should be rounded as well.
+				if (roundClipRect)
+				{
+					clippingSize.Width = LayoutRound(clippingSize.Width);
+					clippingSize.Height = LayoutRound(clippingSize.Height);
+				}
+
+				bool needToClipSlot = IsLessThanAndNotCloseTo(clippingSize.Width, inkSize.Width)
+					|| IsLessThanAndNotCloseTo(clippingSize.Height, inkSize.Height);
+
+
+				if (needToClipSlot)
+				{
+					// The layout clip is created from the slot size determined in the parent's coordinate space,
+					// but is set on the child, meaning it's affected by the child's transform/offset and is applied in
+					// the child's coordinate space.  The inverse of the offset is applied to the clip to prevent the clip's
+					// position from shifting as a result of the change in coordinates.
+					var offset = LayoutHelper.GetAlignmentOffset(this, clippingSize, inkSize);
+					var offsetX = offset.X;
+					var offsetY = offset.Y;
+					if (roundClipRect)
+					{
+						offsetX = LayoutRound(offsetX);
+						offsetY = LayoutRound(offsetY);
+					}
+
+					clipRect = new Rect(-offsetX, -offsetY, clippingSize.Width, clippingSize.Height);
+					if (needToClipLocally)
+					{
+						clipRect = clipRect.IntersectWith(new Rect(0, 0, maxWidthClip, maxHeightClip)) ?? Rect.Empty;
+					}
+				}
+				else if (needToClipLocally)
+				{
+					// In this case clipRect starts at 0, 0 and max width/height clips are rounded due
+					// to RenderSize and MinMax being rounded. So clipRect is already rounded.
+					clipRect.Width = maxWidthClip;
+					clipRect.Height = maxHeightClip;
+				}
+
+				// if we have difference between child and parent FlowDirection
+				// then we have to change origin of Clipping rectangle
+				// which allows us to visually keep it at the same place
+				// UNO TODO
+				//pParent = GetUIElementAdjustedParentInternal(FALSE /*fPublicParentsOnly*/);
+				//if (pParent && pParent->IsRightToLeft() != IsRightToLeft())
+				//{
+				//	clipRect.X = RenderSize.width - (clipRect.X + clipRect.Width);
+				//}
+
+				if (needToClipSlot || needToClipLocally)
+				{
+					if (ShouldApplyLayoutClipAsAncestorClip()
+#if __WASM__
+						&& RenderTransform is { } renderTransform
+#endif
+						)
+					{
+#if __SKIA__
+						clipRect.X += visualOffset.X;
+						clipRect.Y += visualOffset.Y;
+#elif __WASM__
+						clipRect.X -= renderTransform.MatrixCore.M31;
+						clipRect.Y -= renderTransform.MatrixCore.M32;
+#endif
+					}
+
+					return clipRect;
+				}
+			}
+
+			return null;
+		}
+
+		/// <summary>
+		/// Calculates and applies native arrange properties.
+		/// </summary>
+		/// <param name="offset">Offset of the view from its parent</param>
+		/// <param name="clippedFrame">Zone to clip, if clipping is required</param>
+		private void ArrangeNative(Point offset, Rect? clippedFrame)
+		{
+			var newRect = new Rect(offset, RenderSize);
+
+			if (
+				newRect.Width < 0
+				|| newRect.Height < 0
+				|| double.IsNaN(newRect.Width)
+				|| double.IsNaN(newRect.Height)
+				|| double.IsNaN(newRect.X)
+				|| double.IsNaN(newRect.Y)
+			)
+			{
+				throw new InvalidOperationException($"{FormatDebugName()}: Invalid frame size {newRect}. No dimension should be NaN or negative value.");
+			}
+
+#if __SKIA__
+			// clippedFrame here is the one calculated by FrameworkElement.GetClipRect
+			// which propagates to ShapeVisual.ViewBox.
+			// The UIElement.Clip public property isn't considered here on Skia because
+			// it's propagated to Visual.Clip and is set when UIElement.Clip changes.
+			ArrangeVisual(newRect, clippedFrame);
+#else
+			var clip = Clip;
+			var clipRect = clip?.Rect;
+			if (clipRect.HasValue && clip?.Transform is { } transform)
+			{
+				clipRect = transform.TransformBounds(clipRect.Value);
+			}
+
+			if (clipRect.HasValue || clippedFrame.HasValue)
+			{
+				clipRect = (clipRect ?? Rect.Infinite).IntersectWith(clippedFrame ?? Rect.Infinite);
+			}
+
+			ArrangeVisual(newRect, clipRect);
+#endif
+		}
+
+		private string FormatDebugName()
+			=> $"[{this}/{Name}";
+	}
+}
+#endif
diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.cs
index 4b8351833a18..6c8a17b4db49 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkElement.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkElement.cs
@@ -29,7 +29,9 @@
 using Uno.UI.Xaml.Core;
 using Uno.UI.Xaml.Media;
 
+
 #if __ANDROID__
+using Android.Views;
 using View = Android.Views.View;
 #elif __IOS__
 using View = UIKit.UIView;
@@ -46,9 +48,6 @@
 namespace Microsoft.UI.Xaml
 {
 	public partial class FrameworkElement : UIElement, IFrameworkElement, IFrameworkElementInternal, ILayoutConstraints, IDependencyObjectParse
-#if !UNO_REFERENCE_API
-		, ILayouterElement
-#endif
 	{
 		public static class TraceProvider
 		{
@@ -61,16 +60,6 @@ public static class TraceProvider
 			public const int FrameworkElement_InvalidateMeasure = 5;
 		}
 
-#if !UNO_REFERENCE_API
-		private FrameworkElementLayouter _layouter;
-
-		ILayouter ILayouterElement.Layouter => _layouter;
-		Size ILayouterElement.LastAvailableSize => m_previousAvailableSize;
-		bool ILayouterElement.IsMeasureDirty => IsMeasureDirty;
-		bool ILayouterElement.IsFirstMeasureDoneAndManagedElement => IsFirstMeasureDone;
-		bool ILayouterElement.IsMeasureDirtyPathDisabled => IsMeasureDirtyPathDisabled;
-#endif
-
 		private bool _defaultStyleApplied;
 
 		private static readonly Uri DefaultBaseUri = new Uri("ms-appx://local");
@@ -247,7 +236,7 @@ private double ComputeHeightInMinMaxRange(double height)
 		partial void Initialize()
 		{
 #if !UNO_REFERENCE_API
-			_layouter = new FrameworkElementLayouter(this, MeasureOverride, ArrangeOverride);
+			//_layouter = new FrameworkElementLayouter(this, MeasureOverride, ArrangeOverride);
 #endif
 			Resources = new Microsoft.UI.Xaml.ResourceDictionary();
 
@@ -369,11 +358,43 @@ protected virtual Size ArrangeOverride(Size finalSize)
 		/// <returns>The measured size - INCLUDES THE MARGIN</returns>
 		protected Size MeasureElement(View view, Size availableSize)
 		{
-#if UNO_REFERENCE_API
+#if __CROSSRUNTIME__ || IS_UNIT_TESTS
 			view.Measure(availableSize);
 			return view.DesiredSize;
 #else
-			return _layouter.MeasureElement(view, availableSize);
+			if (view is UIElement viewAsUIElement)
+			{
+				viewAsUIElement.Measure(availableSize);
+				return viewAsUIElement.DesiredSize;
+			}
+			else if (view is ILayouterElement layouterElement)
+			{
+				return layouterElement.Measure(availableSize);
+			}
+
+#if __ANDROID__
+			var widthSpec = ViewHelper.MakeMeasureSpec((int)availableSize.Width, Android.Views.MeasureSpecMode.AtMost);
+			var heightSpec = ViewHelper.MakeMeasureSpec((int)availableSize.Height, Android.Views.MeasureSpecMode.AtMost);
+			view.Measure(widthSpec, heightSpec);
+
+			if (view is ViewGroup viewGroup)
+			{
+				var childCount = viewGroup.ChildCount;
+				for (int i = 0; i < childCount; i++)
+				{
+					MeasureElement(viewGroup.GetChildAt(i), availableSize);
+				}
+			}
+#elif __IOS__
+			foreach (var child in view.Subviews)
+			{
+				MeasureElement(child, availableSize);
+			}
+#elif __MACOS
+			// TODO
+#endif
+
+			return availableSize;
 #endif
 		}
 
@@ -384,10 +405,42 @@ protected Size MeasureElement(View view, Size availableSize)
 		/// <param name="finalRect">The final size that the parent computes for the child in layout, provided as a <see cref="Windows.Foundation.Rect"/> value.</param>
 		protected void ArrangeElement(View view, Rect finalRect)
 		{
-#if UNO_REFERENCE_API
+#if __CROSSRUNTIME__
 			view.Arrange(finalRect);
 #else
-			_layouter.ArrangeElement(view, finalRect);
+			if (view is UIElement viewAsUIElement)
+			{
+				viewAsUIElement.Arrange(finalRect);
+			}
+			else if (view is ILayouterElement layouterElement)
+			{
+				layouterElement.Arrange(finalRect);
+			}
+			else
+			{
+				var physicalRect = ViewHelper.LogicalToPhysicalPixels(finalRect);
+#if __ANDROID__
+				view.Layout((int)physicalRect.Left, (int)physicalRect.Top, (int)physicalRect.Right, (int)physicalRect.Bottom);
+
+				if (view is ViewGroup viewGroup)
+				{
+					var childCount = viewGroup.ChildCount;
+					for (int i = 0; i < childCount; i++)
+					{
+						ArrangeElement(viewGroup.GetChildAt(i), finalRect);
+					}
+				}
+#elif __IOS__
+				view.Frame = physicalRect;
+
+				foreach (var subview in view.Subviews)
+				{
+					ArrangeElement(subview, finalRect);
+				}
+#elif __MACOS__
+				// TODO
+#endif
+			}
 #endif
 		}
 
@@ -396,8 +449,8 @@ protected void ArrangeElement(View view, Rect finalRect)
 		/// </summary>
 		protected Size GetElementDesiredSize(View view)
 		{
-#if UNO_REFERENCE_API
-			return view.DesiredSize;
+#if true
+			return (view as UIElement)?.DesiredSize ?? default; // TODO
 #else
 			return (_layouter as ILayouter).GetDesiredSize(view);
 #endif
@@ -1019,34 +1072,34 @@ public AutomationPeer GetAutomationPeer()
 		#endregion
 
 #if !UNO_REFERENCE_API
-		private class FrameworkElementLayouter : Layouter
-		{
-			private readonly MeasureOverrideHandler _measureOverrideHandler;
-			private readonly ArrangeOverrideHandler _arrangeOverrideHandler;
+		//		private class FrameworkElementLayouter : Layouter
+		//		{
+		//			private readonly MeasureOverrideHandler _measureOverrideHandler;
+		//			private readonly ArrangeOverrideHandler _arrangeOverrideHandler;
 
-			public delegate Size ArrangeOverrideHandler(Size finalSize);
-			public delegate Size MeasureOverrideHandler(Size availableSize);
+		//			public delegate Size ArrangeOverrideHandler(Size finalSize);
+		//			public delegate Size MeasureOverrideHandler(Size availableSize);
 
-			public FrameworkElementLayouter(IFrameworkElement element, MeasureOverrideHandler measureOverrideHandler, ArrangeOverrideHandler arrangeOverrigeHandler) : base(element)
-			{
-				_measureOverrideHandler = measureOverrideHandler;
-				_arrangeOverrideHandler = arrangeOverrigeHandler;
-			}
+		//			public FrameworkElementLayouter(IFrameworkElement element, MeasureOverrideHandler measureOverrideHandler, ArrangeOverrideHandler arrangeOverrigeHandler) : base(element)
+		//			{
+		//				_measureOverrideHandler = measureOverrideHandler;
+		//				_arrangeOverrideHandler = arrangeOverrigeHandler;
+		//			}
 
-			public Size MeasureElement(View element, Size availableSize) => MeasureChild(element, availableSize);
+		//			public Size MeasureElement(View element, Size availableSize) => MeasureChild(element, availableSize);
 
-			public void ArrangeElement(View element, Rect finalRect) => ArrangeChild(element, finalRect);
+		//			public void ArrangeElement(View element, Rect finalRect) => ArrangeChild(element, finalRect);
 
-			protected override string Name => Panel.Name;
+		//			protected override string Name => Panel.Name;
 
-			protected override Size ArrangeOverride(Size finalSize) => _arrangeOverrideHandler(finalSize);
+		//			protected override Size ArrangeOverride(Size finalSize) => _arrangeOverrideHandler(finalSize);
 
-#if __ANDROID__
-			protected override void MeasureChild(View view, int widthSpec, int heightSpec) => view.Measure(widthSpec, heightSpec);
-#endif
+		//#if __ANDROID__
+		//			protected override void MeasureChild(View view, int widthSpec, int heightSpec) => view.Measure(widthSpec, heightSpec);
+		//#endif
 
-			protected override Size MeasureOverride(Size availableSize) => _measureOverrideHandler(availableSize);
-		}
+		//			protected override Size MeasureOverride(Size availableSize) => _measureOverrideHandler(availableSize);
+		//		}
 #endif
 	}
 }
diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs
index a9232004dffb..9f08938a878a 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs
@@ -16,108 +16,19 @@ namespace Microsoft.UI.Xaml
 {
 	public partial class FrameworkElement
 	{
-		/// <summary>
-		/// When set, measure and invalidate requests will not be propagated further up the visual tree, ie they won't trigger a relayout.
-		/// Used where repeated unnecessary measure/arrange passes would be unacceptable for performance (eg scrolling in a list).
-		/// </summary>
-		internal bool ShouldInterceptInvalidate { get; set; }
-
 		public override void SetNeedsLayout()
 		{
-			if (ShouldInterceptInvalidate)
-			{
-				return;
-			}
-
-			if (!_inLayoutSubviews)
-			{
-				base.SetNeedsLayout();
-			}
-
-			SetLayoutFlags(LayoutFlag.MeasureDirty | LayoutFlag.ArrangeDirty);
-
-			if (FeatureConfiguration.FrameworkElement.IOsAllowSuperviewNeedsLayoutWhileInLayoutSubViews || !_inLayoutSubviews)
-			{
-				SetSuperviewNeedsLayout();
-			}
+			base.SetNeedsLayout();
 		}
 
 		public override void LayoutSubviews()
 		{
-			try
-			{
-				try
-				{
-					_inLayoutSubviews = true;
-
-					if (IsMeasureDirty)
-					{
-						// Add back the Margin (which is normally 'outside' the view's bounds) - the layouter will subtract it again
-						var availableSizeWithMargins = Bounds.Size.Add(Margin);
-						XamlMeasure(availableSizeWithMargins);
-					}
-
-					//if (IsArrangeDirty) // commented until the MEASURE_DIRTY_PATH is properly implemented for iOS
-					{
-						ClearLayoutFlags(LayoutFlag.ArrangeDirty);
-
-						OnBeforeArrange();
-
-						Rect finalRect;
-						var parent = Superview;
-						if (parent is UIElement or ISetLayoutSlots)
-						{
-							finalRect = LayoutSlotWithMarginsAndAlignments;
-						}
-						else
-						{
-							// Here the "arrange" is coming from a native element,
-							// so we convert those measurements to logical ones.
-							finalRect = RectFromUIRect(Frame);
-
-							// We also need to set the LayoutSlot as it was not by the parent.
-							// Note: This is only an approximation of the LayoutSlot as margin and alignment might already been applied at this point.
-							LayoutInformation.SetLayoutSlot(this, finalRect);
-							LayoutSlotWithMarginsAndAlignments = finalRect;
-						}
-
-						_layouter.Arrange(finalRect);
-
-						OnAfterArrange();
-					}
-				}
-				finally
-				{
-					_inLayoutSubviews = false;
-				}
-			}
-			catch (Exception e)
-			{
-				this.Log().Error($"Layout failed in {GetType()}", e);
-			}
+			base.LayoutSubviews();
 		}
 
 		public override CGSize SizeThatFits(CGSize size)
 		{
-			try
-			{
-				_inLayoutSubviews = true;
-
-				var xamlMeasure = XamlMeasure(size);
-
-				if (xamlMeasure != null)
-				{
-					return _lastMeasure = xamlMeasure.Value;
-				}
-				else
-				{
-					return _lastMeasure = base.SizeThatFits(size);
-				}
-			}
-			finally
-			{
-				_inLayoutSubviews = false;
-			}
+			return base.SizeThatFits(size);
 		}
 
 		public override void AddSubview(UIView view)
diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.iOSmacOS.cs
index e67056244a95..d9304262a86c 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkElement.iOSmacOS.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkElement.iOSmacOS.cs
@@ -39,18 +39,6 @@ partial void OnLoadedPartial()
 
 		private partial void ReconfigureViewportPropagationPartial();
 
-		internal CGSize? XamlMeasure(CGSize availableSize)
-		{
-			if (((ILayouterElement)this).XamlMeasureInternal(availableSize, _lastAvailableSize, out var measuredSize))
-			{
-				_lastAvailableSize = availableSize;
-				_lastMeasure = measuredSize;
-				SetLayoutFlags(LayoutFlag.ArrangeDirty);
-			}
-
-			return _lastMeasure;
-		}
-
 		/// <summary>
 		/// Called before Arrange is called, this method will be deprecated
 		/// once OnMeasure/OnArrange will be implemented completely
diff --git a/src/Uno.UI/UI/Xaml/IFrameworkElement.cs b/src/Uno.UI/UI/Xaml/IFrameworkElement.cs
index 0df45017df84..588078a0c14a 100644
--- a/src/Uno.UI/UI/Xaml/IFrameworkElement.cs
+++ b/src/Uno.UI/UI/Xaml/IFrameworkElement.cs
@@ -446,36 +446,6 @@ public static void MaybeOrNot<TInstance>(this TInstance instance, Action nonNull
 			}
 		}
 
-#if __ANDROID__
-		/// <summary>
-		/// Applies the framework element constraints like the size and max size, using an already measured view.
-		/// </summary>
-		/// <param name="view"></param>
-		public static void OnMeasureOverride<T>(T view)
-			where T : View, IFrameworkElement
-		{
-			var updated = IFrameworkElementHelper
-				.SizeThatFits(view, new _Size(view.MeasuredWidth, view.MeasuredHeight).PhysicalToLogicalPixels())
-				.LogicalToPhysicalPixels();
-
-			Microsoft.UI.Xaml.Controls.Layouter.SetMeasuredDimensions(view, (int)updated.Width, (int)updated.Height);
-		}
-
-		/// <summary>
-		/// Applies the framework element constraints like the size and max size, using the provided measured size.
-		/// </summary>
-		/// <param name="view"></param>
-		public static void OnMeasureOverride<T>(T view, _Size measuredSize)
-			where T : View, IFrameworkElement
-		{
-			var updated = IFrameworkElementHelper
-				.SizeThatFits(view, new _Size(measuredSize.Width, measuredSize.Height).PhysicalToLogicalPixels())
-				.LogicalToPhysicalPixels();
-
-			Microsoft.UI.Xaml.Controls.Layouter.SetMeasuredDimensions(view, (int)updated.Width, (int)updated.Height);
-		}
-#endif
-
 		/// <summary>
 		/// Base constraint reasoning for simple containers that always respect the stretch of their children.
 		/// </summary>
diff --git a/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.Android.tt b/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.Android.tt
index 21e35db05b86..938820928f02 100644
--- a/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.Android.tt
+++ b/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.Android.tt
@@ -42,10 +42,9 @@ namespace <#= mixin.NamespaceName #>
 {
 	partial class <#= mixin.ClassName #> : IFrameworkElement, IXUidProvider, IFrameworkElementInternal
 	{
-		private readonly static IEventProvider _trace = Tracing.Get(FrameworkElement.TraceProvider.Id);
-
 #if !<#= mixin.IsFrameworkElement #> // IsFrameworkElement
 		public event DependencyPropertyChangedEventHandler IsEnabledChanged;
+		private readonly static IEventProvider _trace = Tracing.Get(FrameworkElement.TraceProvider.Id);
 #endif
 
 		public event TypedEventHandler<FrameworkElement, object> Loading;
diff --git a/src/Uno.UI/UI/Xaml/ILayouterElement.Android.cs b/src/Uno.UI/UI/Xaml/ILayouterElement.Android.cs
deleted file mode 100644
index c14b2952473a..000000000000
--- a/src/Uno.UI/UI/Xaml/ILayouterElement.Android.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-#nullable enable
-using System;
-using System.Runtime.CompilerServices;
-using Uno.Extensions;
-using Uno.UI;
-using Windows.Foundation;
-
-namespace Microsoft.UI.Xaml;
-
-internal partial interface ILayouterElement
-{
-	bool StretchAffectsMeasure { get; }
-
-	HorizontalAlignment HorizontalAlignment { get; }
-
-	VerticalAlignment VerticalAlignment { get; }
-
-	[MethodImpl(MethodImplOptions.AggressiveInlining)]
-	internal Size OnMeasureInternal(int widthMeasureSpec, int heightMeasureSpec)
-	{
-		try
-		{
-			var availableSize = ViewHelper.LogicalSizeFromSpec(widthMeasureSpec, heightMeasureSpec);
-
-			this.DoMeasure(availableSize, out var measuredSizeLogical);
-
-			var measuredSize = measuredSizeLogical.LogicalToPhysicalPixels();
-
-			if (StretchAffectsMeasure)
-			{
-				if (HorizontalAlignment == HorizontalAlignment.Stretch &&
-					!double.IsPositiveInfinity(availableSize.Width))
-				{
-					measuredSize.Width = ViewHelper.MeasureSpecGetSize(widthMeasureSpec);
-				}
-
-				if (VerticalAlignment == VerticalAlignment.Stretch && !double.IsPositiveInfinity(availableSize.Height))
-				{
-					measuredSize.Height = ViewHelper.MeasureSpecGetSize(heightMeasureSpec);
-				}
-			}
-
-			// Report our final dimensions.
-			SetMeasuredDimensionInternal(
-				(int)measuredSize.Width,
-				(int)measuredSize.Height
-			);
-
-			return measuredSize;
-		}
-		catch (Exception ex)
-		{
-			Application.Current.RaiseRecoverableUnhandledExceptionOrLog(ex, this);
-			return default;
-		}
-	}
-
-	internal void SetMeasuredDimensionInternal(int width, int height);
-}
diff --git a/src/Uno.UI/UI/Xaml/ILayouterElement.cs b/src/Uno.UI/UI/Xaml/ILayouterElement.cs
index 9d43be9e9e72..7bcac0b54faa 100644
--- a/src/Uno.UI/UI/Xaml/ILayouterElement.cs
+++ b/src/Uno.UI/UI/Xaml/ILayouterElement.cs
@@ -13,130 +13,9 @@ namespace Microsoft.UI.Xaml;
 
 internal partial interface ILayouterElement
 {
-	internal ILayouter Layouter { get; }
+	internal Size Measure(Size availableSize);
 
-	internal Size LastAvailableSize { get; }
-
-	internal bool IsMeasureDirty { get; }
-
-	internal bool IsFirstMeasureDoneAndManagedElement { get; }
-
-	internal bool IsMeasureDirtyPathDisabled { get; }
+	internal void Arrange(Rect finalRect);
 }
 
-internal static class LayouterElementExtensions
-{
-	[MethodImpl(MethodImplOptions.AggressiveInlining)]
-	internal static bool DoMeasure(this ILayouterElement element, Size availableSize, out Size measuredSizeLogical)
-	{
-		var isFirstMeasure = !element.IsFirstMeasureDoneAndManagedElement;
-
-		// "isDirty" here means this element's MeasureOverride
-		// method NEEDS to be called.
-		var isDirty =
-			isFirstMeasure // first time here since attached to parent
-			|| (availableSize != element.LastAvailableSize) // size changed
-			|| element.IsMeasureDirty // .InvalidateMeasure() called
-			|| !FeatureConfiguration.UIElement.UseInvalidateMeasurePath // dirty_path disabled globally
-			|| element.IsMeasureDirtyPathDisabled; // dirty_path disabled locally
-
-		var frameworkElement = element as FrameworkElement;
-		if (frameworkElement is null) // native unmanaged element?
-		{
-			isDirty = true;
-		}
-		else if (!isDirty)
-		{
-			if (!frameworkElement.IsMeasureDirtyPath)
-			{
-				// That's a weird case, but we need to return something meaningful.
-				measuredSizeLogical = frameworkElement.DesiredSize;
-				return false;
-			}
-			if (element.GetParent() is not UIElement and not null)
-			{
-				// If the parent if this element is not managed (UIElement),
-				// .MeasureOverride() needs to be called.
-				isDirty = true;
-			}
-		}
-
-		if (isFirstMeasure)
-		{
-			frameworkElement?.SetLayoutFlags(UIElement.LayoutFlag.FirstMeasureDone);
-		}
-
-		var remainingTries = UIElement.MaxLayoutIterations;
-		measuredSizeLogical = default;
-
-		while (--remainingTries > 0)
-		{
-			if (isDirty || frameworkElement is null)
-			{
-				// We must reset the flag **BEFORE** doing the actual measure, so the elements are able to re-invalidate themselves
-				frameworkElement?.ClearLayoutFlags(UIElement.LayoutFlag.MeasureDirty);
-
-				// The dirty flag is explicitly set on this element
-				try
-				{
-					measuredSizeLogical = element.Layouter.Measure(availableSize);
-				}
-				catch (Exception e)
-				{
-					Application.Current.RaiseRecoverableUnhandledExceptionOrLog(e, element);
-					return false;
-				}
-				finally
-				{
-					LayoutInformation.SetAvailableSize(element, availableSize);
-				}
-
-				return true; // end of isDirty processing
-			}
-
-			// The measure dirty flag is set on one of the descendents:
-			// it will bypass the current element's .MeasureOverride()
-			// since it shouldn't produce a different result and it's
-			// just a waste of precious CPU time to call it.
-			using var children = frameworkElement.GetChildren().GetEnumerator();
-
-			while (children.MoveNext())
-			{
-				var child = children.Current;
-				// If the child is dirty (or is a path to a dirty descendant child),
-				// We're remeasuring it.
-
-				if (child is UIElement { IsMeasureDirtyOrMeasureDirtyPath: true } childAsUIElement)
-				{
-					var previousDesiredSize = childAsUIElement.m_desiredSize;
-					childAsUIElement.EnsureLayoutStorage();
-					element.Layouter.MeasureChild(child, childAsUIElement.m_previousAvailableSize);
-					var newDesiredSize = childAsUIElement.m_desiredSize;
-					if (newDesiredSize != previousDesiredSize)
-					{
-						isDirty = true;
-						break;
-					}
-				}
-				else if (child is not UIElement)
-				{
-					isDirty = true;
-					break;
-				}
-			}
-
-			if (!isDirty)
-			{
-				measuredSizeLogical = LayoutInformation.GetDesiredSize(element);
-				return true; // end of DIRTY_PATH processing
-			}
-
-			// When the end of the loop is reached here, it means the
-			// DIRTY_PATH process has been _upgraded_ to a standard _isDirty
-			// process instead.
-		}
-
-		return false; // UIElement.MaxLayoutIterations reached. Maybe an exception should be raised instead.
-	}
-}
 #endif
diff --git a/src/Uno.UI/UI/Xaml/ILayouterElement.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/ILayouterElement.iOSmacOS.cs
deleted file mode 100644
index 1eede3403417..000000000000
--- a/src/Uno.UI/UI/Xaml/ILayouterElement.iOSmacOS.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-#nullable enable
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using Windows.Foundation;
-using CoreGraphics;
-using Uno.UI;
-
-namespace Microsoft.UI.Xaml;
-
-internal partial interface ILayouterElement
-{
-	[MethodImpl(MethodImplOptions.AggressiveInlining)]
-	internal bool XamlMeasureInternal(
-		Size availableSize,
-		Size? lastAvailableSize,
-		out CGSize measuredSize)
-	{
-		if (IsMeasureDirty || availableSize != lastAvailableSize)
-		{
-			if (this.DoMeasure(availableSize, out var measuredSizeLogical))
-			{
-				measuredSize = measuredSizeLogical.LogicalToPhysicalPixels();
-				return true;
-			}
-		}
-
-		// No need to measure
-		measuredSize = default;
-		return false;
-	}
-}
diff --git a/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs b/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs
index 81653ac7b71c..2f0e455fc18e 100644
--- a/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs
+++ b/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs
@@ -104,4 +104,26 @@ protected override Size ArrangeOverride(Size finalSize)
 
 		return finalSize;
 	}
+
+#if __ANDROID__
+	protected override void OnLayoutCore(bool changed, int left, int top, int right, int bottom, bool localIsLayoutRequested)
+	{
+		base.OnLayoutCore(changed, left, top, right, bottom, localIsLayoutRequested);
+		if (XamlRoot?.HostWindow?.Content is { } content)
+		{
+			content.UpdateLayout();
+		}
+	}
+#elif __IOS__
+
+	public override void LayoutIfNeeded()
+	{
+		base.LayoutIfNeeded();
+
+		if (XamlRoot?.HostWindow?.Content is { } content)
+		{
+			content.UpdateLayout();
+		}
+	}
+#endif
 }
diff --git a/src/Uno.UI/UI/Xaml/LayoutStorage.cs b/src/Uno.UI/UI/Xaml/LayoutStorage.cs
index 9a413e5d42c7..a2c600e411ce 100644
--- a/src/Uno.UI/UI/Xaml/LayoutStorage.cs
+++ b/src/Uno.UI/UI/Xaml/LayoutStorage.cs
@@ -22,10 +22,9 @@ partial class UIElement
 	internal Size m_desiredSize;
 	internal Rect m_finalRect;
 	//internal Point m_offset;
-#if UNO_REFERENCE_API
-	// On mobile, stored in Layouter
+
 	internal Size m_unclippedDesiredSize;
-#endif
+
 	internal Size m_size;
 
 	// Stores the layout clip, which is always an axis-aligned rect.
diff --git a/src/Uno.UI/UI/Xaml/Shapes/Shape.Android.cs b/src/Uno.UI/UI/Xaml/Shapes/Shape.Android.cs
index 80cd0fb2b8cc..a039aab170fe 100644
--- a/src/Uno.UI/UI/Xaml/Shapes/Shape.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Shapes/Shape.Android.cs
@@ -37,17 +37,17 @@ protected override void OnDraw(Canvas canvas)
 			}
 
 			//Drawing paths on the canvas does not respect the canvas' ClipBounds
-			if (ClippedFrame is { } clippedFrame)
-			{
-				clippedFrame = clippedFrame.LogicalToPhysicalPixels();
-				if (FrameRoundingAdjustment is { } fra)
-				{
-					clippedFrame.Width += fra.Width;
-					clippedFrame.Height += fra.Height;
-				}
-
-				canvas.ClipRect(clippedFrame.ToRectF());
-			}
+			//if (ClippedFrame is { } clippedFrame)
+			//{
+			//	clippedFrame = clippedFrame.LogicalToPhysicalPixels();
+			//	if (FrameRoundingAdjustment is { } fra)
+			//	{
+			//		clippedFrame.Width += fra.Width;
+			//		clippedFrame.Height += fra.Height;
+			//	}
+
+			//	canvas.ClipRect(clippedFrame.ToRectF());
+			//}
 
 			DrawFill(canvas);
 			DrawStroke(canvas);
diff --git a/src/Uno.UI/UI/Xaml/UIElement.Android.cs b/src/Uno.UI/UI/Xaml/UIElement.Android.cs
index 581d576f0faf..b15120b9bd61 100644
--- a/src/Uno.UI/UI/Xaml/UIElement.Android.cs
+++ b/src/Uno.UI/UI/Xaml/UIElement.Android.cs
@@ -106,30 +106,6 @@ public UIElement()
 			InitializePointers();
 		}
 
-		/// <summary>
-		/// On Android, the equivalent of the "Dirty Path" is the native
-		/// "Layout Requested" mechanism.
-		/// </summary>
-		internal bool IsMeasureDirtyPath
-		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutRequested;
-		}
-
-		/// <summary>
-		/// Determines if InvalidateArrange has been called
-		/// </summary>
-		internal bool IsArrangeDirty => IsLayoutRequested;
-
-		/// <summary>
-		/// Not implemented yet on this platform.
-		/// </summary>
-		internal bool IsArrangeDirtyPath
-		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => false;
-		}
-
 		/// <summary>
 		/// Gets the **logical** frame (a.k.a. 'finalRect') of the element while it's being arranged by a managed parent.
 		/// </summary>
@@ -203,6 +179,18 @@ partial void ApplyNativeClip(Rect rect)
 			}
 		}
 
+		internal void ArrangeVisual(Rect finalRect, Rect? clippedFrame = default)
+		{
+			// TODO: clipped frame?
+			var physical = finalRect.LogicalToPhysicalPixels();
+			this.Layout(
+				(int)physical.Left,
+				(int)physical.Top,
+				(int)physical.Right,
+				(int)physical.Bottom
+			);
+		}
+
 		/// <summary>
 		/// This method is called from the OnDraw of elements supporting rounded corners:
 		/// Border, Rectangle, Panel...
diff --git a/src/Uno.UI/UI/Xaml/UIElement.Layout.Flags.cs b/src/Uno.UI/UI/Xaml/UIElement.Layout.Flags.cs
new file mode 100644
index 000000000000..856062c3ac86
--- /dev/null
+++ b/src/Uno.UI/UI/Xaml/UIElement.Layout.Flags.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Microsoft.UI.Xaml
+{
+	partial class UIElement
+	{
+		/// <summary>
+		/// Determines if InvalidateMeasure has been called
+		/// </summary>
+		internal bool IsMeasureDirty
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.MeasureDirty);
+		}
+
+		internal bool IsMeasureDirtyPath
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.MeasureDirtyPath);
+		}
+
+		internal bool IsMeasureDirtyOrMeasureDirtyPath
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsAnyLayoutFlagsSet(LayoutFlag.MeasureDirty | LayoutFlag.MeasureDirtyPath);
+		}
+
+		/// <summary>
+		/// If the first measure has been done since the control
+		/// is connected to its parent
+		/// </summary>
+		internal bool IsFirstMeasureDone
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.FirstMeasureDone);
+		}
+
+		/// <summary>
+		/// Determines if InvalidateArrange has been called
+		/// </summary>
+		internal bool IsArrangeDirty
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.ArrangeDirty);
+		}
+
+		internal bool IsArrangeDirtyPath
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsAnyLayoutFlagsSet(LayoutFlag.ArrangeDirtyPath);
+		}
+
+		internal bool IsArrangeDirtyOrArrangeDirtyPath
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsAnyLayoutFlagsSet(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
+		}
+
+		/// <summary>
+		/// If the first arrange has been done since the control
+		/// is connected to its parent
+		/// </summary>
+		internal bool IsFirstArrangeDone
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.FirstArrangeDone);
+		}
+
+		[Flags]
+		internal enum LayoutFlag : short
+		{
+			/// <summary>
+			/// Means the Measure is dirty for the current element
+			/// </summary>
+			MeasureDirty = 0b0000_0001,
+
+			/// <summary>
+			/// Means the Measure is dirty on at least one child of this element
+			/// </summary>
+			MeasureDirtyPath = 0b0000_0010,
+
+			/// <summary>
+			/// Indicates that the element is currently being measured during the Arrange cycle.
+			/// </summary>
+			MeasureDuringArrange = 0b0000_0001_0000_0000,
+
+			/// <summary>
+			/// Indicates that the element is currently being measured.
+			/// </summary>
+			MeasuringSelf = 0b0000_0010_0000_0000,
+
+			/// <summary>
+			/// Indicates that first measure has been done on the element after been connected to parent
+			/// </summary>
+			FirstMeasureDone = 0b0000_0100,
+
+			/// <summary>
+			/// Means the MeasureDirtyPath is disabled on this element.
+			/// </summary>
+			/// <remarks>
+			/// This flag is copied to children when they are attached, but can be re-enabled afterwards.
+			/// This flag is used during invalidation
+			/// </remarks>
+			MeasureDirtyPathDisabled = 0b0000_1000,
+
+			/// <summary>
+			/// Means the Arrange is dirty on the current element or one of its child
+			/// </summary>
+			ArrangeDirty = 0b0001_0000,
+
+			/// <summary>
+			/// Means the Arrange is dirty on at least one child of this element
+			/// </summary>
+			ArrangeDirtyPath = 0b0010_0000,
+
+			/// <summary>
+			/// Indicates that first arrange has been done on the element and we can use the
+			/// LayoutInformation.GetLayoutSlot() to get previous finalRect
+			/// </summary>
+			FirstArrangeDone = 0b0100_0000,
+
+			/// <summary>
+			/// Means the MeasureDirtyPath is disabled on this element.
+			/// </summary>
+			/// <remarks>
+			/// This flag is copied to children when they are attached, but can be re-enabled afterwards.
+			/// This flag is used during invalidation
+			/// </remarks>
+			ArrangeDirtyPathDisabled = 0b1000_0000,
+		}
+
+		private const LayoutFlag DEFAULT_STARTING_LAYOUTFLAGS = 0;
+		private const LayoutFlag LAYOUT_FLAGS_TO_CLEAR_ON_RESET =
+			LayoutFlag.MeasureDirty |
+			LayoutFlag.MeasureDirtyPath |
+			LayoutFlag.MeasureDuringArrange |
+			LayoutFlag.MeasuringSelf |
+			LayoutFlag.ArrangeDirty |
+			LayoutFlag.ArrangeDirtyPath |
+			LayoutFlag.FirstArrangeDone |
+			LayoutFlag.FirstMeasureDone;
+
+		private LayoutFlag _layoutFlags = DEFAULT_STARTING_LAYOUTFLAGS;
+
+		/// <summary>
+		/// Check for one specific layout flag
+		/// </summary>
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal bool IsLayoutFlagSet(LayoutFlag flag) => (_layoutFlags & flag) == flag;
+
+		/// <summary>
+		/// Check that at least one of the specified flags is set
+		/// </summary>
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal bool IsAnyLayoutFlagsSet(LayoutFlag flags) => (_layoutFlags & flags) != 0;
+
+		/// <summary>
+		/// Set one or many flags (set to 1)
+		/// </summary>
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal void SetLayoutFlags(LayoutFlag flags) => _layoutFlags |= flags;
+
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal void SetLayoutFlags(LayoutFlag flags, bool state)
+		{
+			if (state)
+			{
+				SetLayoutFlags(flags);
+			}
+			else
+			{
+				ClearLayoutFlags(flags);
+			}
+		}
+
+		/// <summary>
+		/// Reset one or many flags (set flag to zero)
+		/// </summary>
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal void ClearLayoutFlags(LayoutFlag flags) => _layoutFlags &= ~flags;
+
+		/// <summary>
+		/// Reset flags to original state
+		/// </summary>
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal void ResetLayoutFlags() => ClearLayoutFlags(LAYOUT_FLAGS_TO_CLEAR_ON_RESET);
+
+		internal bool IsMeasureDirtyPathDisabled
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.MeasureDirtyPathDisabled);
+
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			set => SetLayoutFlags(LayoutFlag.MeasureDirtyPathDisabled, value);
+		}
+
+		internal bool IsArrangeDirtyPathDisabled
+		{
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			get => IsLayoutFlagSet(LayoutFlag.ArrangeDirtyPathDisabled);
+
+			[MethodImpl(MethodImplOptions.AggressiveInlining)]
+			set => SetLayoutFlags(LayoutFlag.ArrangeDirtyPathDisabled, value);
+		}
+	}
+}
diff --git a/src/Uno.UI/UI/Xaml/UIElement.Layout.crossruntime.cs b/src/Uno.UI/UI/Xaml/UIElement.Layout.crossruntime.cs
deleted file mode 100644
index b45f26c83a6c..000000000000
--- a/src/Uno.UI/UI/Xaml/UIElement.Layout.crossruntime.cs
+++ /dev/null
@@ -1,498 +0,0 @@
-#if !__NETSTD_REFERENCE__
-using System;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Controls.Primitives;
-using Uno.Foundation.Logging;
-using Uno.UI;
-using Uno.UI.Extensions;
-using Uno.UI.Xaml;
-using Windows.Foundation;
-
-namespace Microsoft.UI.Xaml
-{
-	public partial class UIElement : DependencyObject
-	{
-		/// <summary>
-		/// When set, measure and invalidate requests will not be propagated further up the visual tree, ie they won't trigger a re-layout.
-		/// Used where repeated unnecessary measure/arrange passes would be unacceptable for performance (eg scrolling in a list).
-		/// </summary>
-		internal bool ShouldInterceptInvalidate { get; set; }
-
-		public void InvalidateMeasure()
-		{
-			if (ShouldInterceptInvalidate || IsMeasureDirty || IsLayoutFlagSet(LayoutFlag.MeasuringSelf))
-			{
-				return;
-			}
-
-			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
-			{
-				this.Log().LogWarning($"[LayoutCycleTracing] InvalidateMeasure {this},{this.GetDebugName()}");
-			}
-
-			SetLayoutFlags(LayoutFlag.MeasureDirty);
-
-			if (FeatureConfiguration.UIElement.UseInvalidateMeasurePath && !IsMeasureDirtyPathDisabled)
-			{
-				InvalidateParentMeasureDirtyPath();
-			}
-			else
-			{
-				(this.GetParent() as UIElement)?.InvalidateMeasure();
-				if (IsVisualTreeRoot)
-				{
-					XamlRoot.InvalidateMeasure();
-				}
-			}
-		}
-
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void InvalidateMeasureDirtyPath()
-		{
-			if (IsMeasureDirtyOrMeasureDirtyPath)
-			{
-				return; // Already invalidated
-			}
-
-			SetLayoutFlags(LayoutFlag.MeasureDirtyPath);
-
-			InvalidateParentMeasureDirtyPath();
-		}
-
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal void InvalidateParentMeasureDirtyPath()
-		{
-			if (this.GetParent() is UIElement parent) //TODO: Should this use VisualTree.GetParent as fallback? https://github.com/unoplatform/uno/issues/8978
-			{
-				parent.InvalidateMeasureDirtyPath();
-			}
-			else if (IsVisualTreeRoot)
-			{
-				XamlRoot.InvalidateMeasure();
-			}
-		}
-
-		public void InvalidateArrange()
-		{
-			if (ShouldInterceptInvalidate)
-			{
-				return;
-			}
-
-			if (IsArrangeDirty)
-			{
-				return; // Already dirty
-			}
-
-			if (_traceLayoutCycle)
-			{
-				if (this.Log().IsEnabled(LogLevel.Error))
-				{
-					this.Log().LogError($"[LayoutCycleTracing] InvalidateArrange {this},{this.GetDebugName()}");
-				}
-
-				if (this.Log().IsEnabled(LogLevel.Trace))
-				{
-					this.Log().Trace($"[LayoutCycleTracing] {Environment.StackTrace}");
-				}
-			}
-
-			SetLayoutFlags(LayoutFlag.ArrangeDirty);
-
-			if (FeatureConfiguration.UIElement.UseInvalidateArrangePath && !IsArrangeDirtyPathDisabled)
-			{
-				InvalidateParentArrangeDirtyPath();
-			}
-			else
-			{
-				(this.GetParent() as UIElement)?.InvalidateArrange();
-				if (IsVisualTreeRoot)
-				{
-					XamlRoot.InvalidateArrange();
-				}
-			}
-		}
-
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void InvalidateArrangeDirtyPath()
-		{
-			if (IsArrangeDirtyOrArrangeDirtyPath)
-			{
-				return; // Already invalidated
-			}
-
-			SetLayoutFlags(LayoutFlag.ArrangeDirtyPath);
-
-			InvalidateParentArrangeDirtyPath();
-		}
-
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void InvalidateParentArrangeDirtyPath()
-		{
-			if (this.GetParent() is UIElement parent) //TODO: Should this use VisualTree.GetParent as fallback? https://github.com/unoplatform/uno/issues/8978
-			{
-				parent.InvalidateArrangeDirtyPath();
-			}
-			else //TODO: Why not check IsVisualTreeRoot as in InvalidateParentMeasureDirtyPath?
-			{
-				XamlRoot?.InvalidateArrange();
-			}
-		}
-
-		public void Measure(Size availableSize)
-		{
-			if (!_isFrameworkElement)
-			{
-				return; // Only FrameworkElements are measurable
-			}
-
-			// Visibility should not be checked here. Consider the following scenario:
-			// 1. A collapsed element is measured before it enters the visual tree
-			// 2. Visibility changes to Visible after it enters the visual tree, which will call InvalidateMeasure
-			// In this case, we want step 1 to clear the dirty flag.
-			// If the flag isn't cleared in step 1, then InvalidateMeasure call in step 2 will do nothing because the
-			// element is already dirty, which means the dirtiness isn't propagated up to RootVisual.
-			// So, we want to go into DoMeasure, which will clear the flag.
-			// Then, DoMeasure is going to early return if Visibility is collapsed.
-
-			if (IsVisualTreeRoot)
-			{
-				MeasureVisualTreeRoot(availableSize);
-			}
-			else
-			{
-				// If possible we avoid the try/finally which might be costly on some platforms
-				DoMeasure(availableSize);
-			}
-		}
-
-		/// <remarks>
-		/// This method contains or is called by a try/catch containing method and
-		/// can be significantly slower than other methods as a result on WebAssembly.
-		/// See https://github.com/dotnet/runtime/issues/56309
-		/// </remarks>
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void MeasureVisualTreeRoot(Size availableSize)
-		{
-			try
-			{
-				_isLayoutingVisualTreeRoot = true;
-				DoMeasure(availableSize);
-			}
-			finally
-			{
-				_isLayoutingVisualTreeRoot = false;
-			}
-		}
-
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void DoMeasure(Size availableSize)
-		{
-			var isFirstMeasure = !IsLayoutFlagSet(LayoutFlag.FirstMeasureDone);
-
-			var isDirty =
-				isFirstMeasure
-				|| (availableSize != m_previousAvailableSize)
-				|| IsMeasureDirty
-				|| !FeatureConfiguration.UIElement.UseInvalidateMeasurePath // dirty_path disabled globally
-				|| IsMeasureDirtyPathDisabled;
-
-			var isMeasureDirtyPath = IsMeasureDirtyPath;
-
-			if (!isDirty && !isMeasureDirtyPath)
-			{
-				return; // Nothing to do
-			}
-
-			if (isFirstMeasure)
-			{
-				SetLayoutFlags(LayoutFlag.FirstMeasureDone);
-			}
-
-			var remainingTries = MaxLayoutIterations;
-
-			while (--remainingTries > 0)
-			{
-				if (isDirty)
-				{
-					// We must reset the flag **BEFORE** doing the actual measure, so the elements are able to re-invalidate themselves
-					// TODO: This doesn't actually follow WinUI. It looks like in WinUI, the method
-					// CUIElement::MeasureInternal is doing SetIsMeasureDirty(FALSE); at the end.
-					// If we were able to align this to WinUI, we should remember clearing
-					// the flag in the Visibility == Visibility.Collapsed case as well.
-					// The Visibility condition is similar to the GetIsLayoutSuspended check in
-					// WinUI, which does goto Cleanup and will call SetIsMeasureDirty(FALSE);
-					ClearLayoutFlags(LayoutFlag.MeasureDirty | LayoutFlag.MeasureDirtyPath);
-
-					var prevSize = DesiredSize;
-
-					// The dirty flag is explicitly set on this element
-#if DEBUG
-					try
-#endif
-					{
-						if (this.Visibility == Visibility.Collapsed)
-						{
-							m_desiredSize = default;
-							RecursivelyApplyTemplateWorkaround();
-							return;
-						}
-
-						SetLayoutFlags(LayoutFlag.MeasuringSelf);
-						MeasureCore(availableSize);
-						ClearLayoutFlags(LayoutFlag.MeasuringSelf);
-						InvalidateArrange();
-					}
-#if DEBUG
-					catch (Exception ex)
-					{
-						_log.Error($"Error measuring {this}", ex);
-						throw;
-					}
-					finally
-#endif
-					{
-						m_previousAvailableSize = availableSize;
-
-						// if (!GetIsMeasureDuringArrange() && ! IsSameSize(prevSize, desiredSize) && !bInLayoutTransition)
-						if (!IsLayoutFlagSet(LayoutFlag.MeasureDuringArrange) && prevSize != DesiredSize)
-						{
-							if (GetUIElementAdjustedParentInternal() is { } pParent)
-							{
-								pParent.OnChildDesiredSizeChanged(pParent);
-							}
-						}
-					}
-
-					break;
-				}
-
-				// isMeasureDirtyPath is always true here
-				ClearLayoutFlags(LayoutFlag.MeasureDirtyPath);
-
-				// The dirty flag is set on one of the descendents:
-				// it will bypass the current element's MeasureOverride()
-				// since it shouldn't produce a different result and it's
-				// just a waste of precious CPU time to call it.
-				var children = GetChildren().GetEnumerator();
-
-				//foreach (var child in children)
-				while (children.MoveNext())
-				{
-					if (children.Current is { IsMeasureDirtyOrMeasureDirtyPath: true } child)
-					{
-						// If the child is dirty (or is a path to a dirty descendant child),
-						// We're remeasuring it.
-
-						var previousDesiredSize = child.DesiredSize;
-						child.EnsureLayoutStorage();
-						child.Measure(child.m_previousAvailableSize);
-						if (child.DesiredSize != previousDesiredSize)
-						{
-							isDirty = true;
-							break;
-						}
-					}
-				}
-
-				children.Dispose(); // no "using" operator here to prevent an implicit try-catch on Wasm
-
-				if (isDirty)
-				{
-					continue;
-				}
-
-				break;
-			}
-		}
-
-		internal virtual void MeasureCore(Size availableSize)
-		{
-			throw new NotSupportedException("UIElement doesn't implement MeasureCore. Inherit from FrameworkElement, which properly implements MeasureCore.");
-		}
-
-		internal bool ShouldApplyLayoutClipAsAncestorClip()
-		{
-			return this is Panel; // Restrict to Panels, to limit app-compat risk
-								  //&& !GetIsScrollViewerHeader(); // Special-case:  ScrollViewer Headers, which can zoom, must scale the LayoutClip too
-		}
-
-		private void RecursivelyApplyTemplateWorkaround()
-		{
-			// Uno workaround. The template should NOT be applied here.
-			// But, without this workaround, VerifyVisibilityChangeUpdatesCommandBarVisualState test will fail.
-			// The real root cause for the test failure is that FindParentCommandBarForElement will
-			// return null, that is because Uno doesn't yet properly have a "logical parent" concept.
-			// We eagerly apply the template so that FindParentCommandBarForElement will
-			// find the command bar through TemplatedParent
-			if (this is Control thisAsControl)
-			{
-				thisAsControl.TryCallOnApplyTemplate();
-
-				// Update bindings to ensure resources defined
-				// in visual parents get applied.
-				this.UpdateResourceBindings();
-			}
-
-			foreach (var child in _children)
-			{
-				child.RecursivelyApplyTemplateWorkaround();
-			}
-		}
-
-
-		public void Arrange(Rect finalRect)
-		{
-			if (!_isFrameworkElement)
-			{
-				return;
-			}
-
-			var firstArrangeDone = IsFirstArrangeDone;
-
-			if (Visibility == Visibility.Collapsed)
-			{
-				m_finalRect = finalRect;
-				HideVisual();
-				ClearLayoutFlags(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
-				return;
-			}
-
-			if (firstArrangeDone && !IsArrangeDirtyOrArrangeDirtyPath && finalRect == m_finalRect)
-			{
-				ClearLayoutFlags(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
-				return; // Calling Arrange would be a waste of CPU time here.
-			}
-
-			if (IsVisualTreeRoot)
-			{
-				ArrangeVisualTreeRoot(finalRect);
-			}
-			else
-			{
-				// If possible we avoid the try/finally which might be costly on some platforms
-				DoArrange(finalRect);
-			}
-		}
-
-		/// <remarks>
-		/// This method contains or is called by a try/catch containing method and can be significantly slower than other methods as a result on WebAssembly.
-		/// See https://github.com/dotnet/runtime/issues/56309
-		/// </remarks>
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void ArrangeVisualTreeRoot(Rect finalRect)
-		{
-			try
-			{
-				_isLayoutingVisualTreeRoot = true;
-				DoArrange(finalRect);
-			}
-			finally
-			{
-				_isLayoutingVisualTreeRoot = false;
-			}
-		}
-
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		private void DoArrange(Rect finalRect)
-		{
-			var isFirstArrange = !IsLayoutFlagSet(LayoutFlag.FirstArrangeDone);
-
-			var isDirty =
-				isFirstArrange
-				|| IsArrangeDirty
-				|| finalRect != m_finalRect;
-
-			if (!isDirty && !IsArrangeDirtyPath)
-			{
-				return; // Nothing do to
-			}
-
-			if (GetUseLayoutRounding())
-			{
-				finalRect.X = LayoutRound(finalRect.X);
-				finalRect.Y = LayoutRound(finalRect.Y);
-				finalRect.Width = LayoutRound(finalRect.Width);
-				finalRect.Height = LayoutRound(finalRect.Height);
-			}
-
-			var remainingTries = MaxLayoutIterations;
-
-			while (--remainingTries > 0)
-			{
-				if (IsMeasureDirtyOrMeasureDirtyPath)
-				{
-					// Uno doc: in WinUI, the flag is only set and reset if IsMeasureDirty, not IsMeasureDirtyOrMeasureDirtyPath
-					SetLayoutFlags(LayoutFlag.MeasureDuringArrange);
-					DoMeasure(m_previousAvailableSize);
-					ClearLayoutFlags(LayoutFlag.MeasureDuringArrange);
-				}
-
-				if (isDirty)
-				{
-					ShowVisual();
-
-					// We must store the updated slot before natively arranging the element,
-					// so the updated value can be read by indirect code that is being invoked on arrange.
-					// For instance, the EffectiveViewPort computation reads that value to detect slot changes (cf. PropagateEffectiveViewportChange)
-					m_finalRect = finalRect;
-
-					// We must reset the flag **BEFORE** doing the actual arrange, so the elements are able to re-invalidate themselves
-					ClearLayoutFlags(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
-
-					ArrangeCore(finalRect);
-
-					SetLayoutFlags(LayoutFlag.FirstArrangeDone);
-
-					break;
-				}
-				else if (IsArrangeDirtyPath)
-				{
-					ClearLayoutFlags(LayoutFlag.ArrangeDirtyPath);
-
-					var children = GetChildren().GetEnumerator();
-
-					while (children.MoveNext())
-					{
-						var child = children.Current;
-
-						if (child is { IsArrangeDirtyOrArrangeDirtyPath: true })
-						{
-							var previousRenderSize = child.RenderSize;
-							child.Arrange(child.m_finalRect);
-
-							if (child.RenderSize != previousRenderSize)
-							{
-								isDirty = true;
-								break;
-							}
-						}
-					}
-
-					children.Dispose(); // no "using" operator here to prevent an implicit try-catch on Wasm
-
-					if (!isDirty)
-					{
-						break;
-					}
-				}
-				else
-				{
-					break;
-				}
-			}
-
-		}
-
-		partial void HideVisual();
-		partial void ShowVisual();
-
-		internal virtual void ArrangeCore(Rect finalRect)
-		{
-			throw new NotSupportedException("UIElement doesn't implement ArrangeCore. Inherit from FrameworkElement, which properly implements ArrangeCore.");
-		}
-	}
-}
-#endif
diff --git a/src/Uno.UI/UI/Xaml/UIElement.Layout.cs b/src/Uno.UI/UI/Xaml/UIElement.Layout.cs
index 63465f100732..aec9b4b9409f 100644
--- a/src/Uno.UI/UI/Xaml/UIElement.Layout.cs
+++ b/src/Uno.UI/UI/Xaml/UIElement.Layout.cs
@@ -1,255 +1,534 @@
-#if __WASM__ || __SKIA__
-// "Managed Measure Dirty Path" means it's the responsibility of the
-// managed code (Uno) to walk the tree and do the measure phase.
-#define IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
-#define IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
-#endif
+#if !__NETSTD_REFERENCE__
 using System;
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Uno.Foundation.Logging;
+using Uno.UI;
+using Uno.UI.Extensions;
+using Uno.UI.Xaml;
+using Windows.Foundation;
 
 namespace Microsoft.UI.Xaml
 {
-	partial class UIElement
+	public partial class UIElement : DependencyObject
 	{
 		/// <summary>
-		/// Determines if InvalidateMeasure has been called
+		/// When set, measure and invalidate requests will not be propagated further up the visual tree, ie they won't trigger a re-layout.
+		/// Used where repeated unnecessary measure/arrange passes would be unacceptable for performance (eg scrolling in a list).
 		/// </summary>
-		internal bool IsMeasureDirty
+		internal bool ShouldInterceptInvalidate { get; set; }
+
+		public void InvalidateMeasure()
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.MeasureDirty);
+			if (ShouldInterceptInvalidate || IsMeasureDirty || IsLayoutFlagSet(LayoutFlag.MeasuringSelf))
+			{
+				return;
+			}
+
+			if (_traceLayoutCycle && this.Log().IsEnabled(LogLevel.Warning))
+			{
+				this.Log().LogWarning($"[LayoutCycleTracing] InvalidateMeasure {this},{this.GetDebugName()}");
+			}
+
+			SetLayoutFlags(LayoutFlag.MeasureDirty);
+
+			if (FeatureConfiguration.UIElement.UseInvalidateMeasurePath && !IsMeasureDirtyPathDisabled)
+			{
+				InvalidateParentMeasureDirtyPath();
+			}
+			else
+			{
+				(this.GetParent() as UIElement)?.InvalidateMeasure();
+				if (IsVisualTreeRoot)
+				{
+#if __CROSSRUNTIME__
+					XamlRoot.InvalidateMeasure();
+#endif
+				}
+			}
 		}
 
-#if IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
-		internal bool IsMeasureDirtyPath
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private void InvalidateMeasureDirtyPath()
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.MeasureDirtyPath);
+			if (IsMeasureDirtyOrMeasureDirtyPath)
+			{
+				return; // Already invalidated
+			}
+
+			SetLayoutFlags(LayoutFlag.MeasureDirtyPath);
+
+			InvalidateParentMeasureDirtyPath();
 		}
-#else
-		// IsMeasureDirtyPath implemented in platform-specific file to
-		// connect to native mechanisms.
-#endif
 
-#if IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
-		internal bool IsMeasureDirtyOrMeasureDirtyPath
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		internal void InvalidateParentMeasureDirtyPath()
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsAnyLayoutFlagsSet(LayoutFlag.MeasureDirty | LayoutFlag.MeasureDirtyPath);
+			if (this.GetParent() is UIElement parent) //TODO: Should this use VisualTree.GetParent as fallback? https://github.com/unoplatform/uno/issues/8978
+			{
+				parent.InvalidateMeasureDirtyPath();
+			}
+			else if (IsVisualTreeRoot)
+			{
+#if __CROSSRUNTIME__
+				XamlRoot.InvalidateMeasure();
+#endif
+			}
 		}
-#else
-		/// <summary>
-		/// This is for compatibility - not implemented yet on this platform
-		/// </summary>
-		internal bool IsMeasureDirtyOrMeasureDirtyPath
+
+		public void InvalidateArrange()
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsMeasureDirty || IsMeasureDirtyPath;
-		}
+			if (ShouldInterceptInvalidate)
+			{
+				return;
+			}
+
+			if (IsArrangeDirty)
+			{
+				return; // Already dirty
+			}
+
+			if (_traceLayoutCycle)
+			{
+				if (this.Log().IsEnabled(LogLevel.Error))
+				{
+					this.Log().LogError($"[LayoutCycleTracing] InvalidateArrange {this},{this.GetDebugName()}");
+				}
+
+				if (this.Log().IsEnabled(LogLevel.Trace))
+				{
+					this.Log().Trace($"[LayoutCycleTracing] {Environment.StackTrace}");
+				}
+			}
+
+			SetLayoutFlags(LayoutFlag.ArrangeDirty);
+
+			if (FeatureConfiguration.UIElement.UseInvalidateArrangePath && !IsArrangeDirtyPathDisabled)
+			{
+				InvalidateParentArrangeDirtyPath();
+			}
+			else
+			{
+				(this.GetParent() as UIElement)?.InvalidateArrange();
+				if (IsVisualTreeRoot)
+				{
+#if __CROSSRUNTIME__
+					XamlRoot.InvalidateArrange();
+#elif __ANDROID__
+					this.RequestLayout();
+#elif __IOS__
+					this.SetNeedsLayout();
+#elif __MACOS__
+					// TODO
 #endif
+				}
+			}
+		}
 
-		/// <summary>
-		/// If the first measure has been done since the control
-		/// is connected to its parent
-		/// </summary>
-		internal bool IsFirstMeasureDone
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private void InvalidateArrangeDirtyPath()
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.FirstMeasureDone);
+			if (IsArrangeDirtyOrArrangeDirtyPath)
+			{
+				return; // Already invalidated
+			}
+
+			SetLayoutFlags(LayoutFlag.ArrangeDirtyPath);
+
+			InvalidateParentArrangeDirtyPath();
 		}
 
-#if !__ANDROID__
-		/// <summary>
-		/// Determines if InvalidateArrange has been called
-		/// </summary>
-		internal bool IsArrangeDirty
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private void InvalidateParentArrangeDirtyPath()
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.ArrangeDirty);
-		}
+			if (this.GetParent() is UIElement parent) //TODO: Should this use VisualTree.GetParent as fallback? https://github.com/unoplatform/uno/issues/8978
+			{
+				parent.InvalidateArrangeDirtyPath();
+			}
+			else //TODO: Why not check IsVisualTreeRoot as in InvalidateParentMeasureDirtyPath?
+			{
+#if __CROSSRUNTIME__
+				XamlRoot?.InvalidateArrange();
+#elif __ANDROID__
+				RequestLayout();
 #endif
+			}
+		}
 
-#if IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
-		internal bool IsArrangeDirtyPath
+		public void Measure(Size availableSize)
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsAnyLayoutFlagsSet(LayoutFlag.ArrangeDirtyPath);
+#if __CROSSRUNTIME__
+			if (!_isFrameworkElement)
+#else
+			if (this is not FrameworkElement)
+#endif
+			{
+				return; // Only FrameworkElements are measurable
+			}
+
+			// Visibility should not be checked here. Consider the following scenario:
+			// 1. A collapsed element is measured before it enters the visual tree
+			// 2. Visibility changes to Visible after it enters the visual tree, which will call InvalidateMeasure
+			// In this case, we want step 1 to clear the dirty flag.
+			// If the flag isn't cleared in step 1, then InvalidateMeasure call in step 2 will do nothing because the
+			// element is already dirty, which means the dirtiness isn't propagated up to RootVisual.
+			// So, we want to go into DoMeasure, which will clear the flag.
+			// Then, DoMeasure is going to early return if Visibility is collapsed.
+
+			if (IsVisualTreeRoot)
+			{
+				MeasureVisualTreeRoot(availableSize);
+			}
+			else
+			{
+				// If possible we avoid the try/finally which might be costly on some platforms
+				DoMeasure(availableSize);
+			}
 		}
 
-		internal bool IsArrangeDirtyOrArrangeDirtyPath
+		/// <remarks>
+		/// This method contains or is called by a try/catch containing method and
+		/// can be significantly slower than other methods as a result on WebAssembly.
+		/// See https://github.com/dotnet/runtime/issues/56309
+		/// </remarks>
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private void MeasureVisualTreeRoot(Size availableSize)
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsAnyLayoutFlagsSet(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
+			try
+			{
+				_isLayoutingVisualTreeRoot = true;
+				DoMeasure(availableSize);
+			}
+			finally
+			{
+				_isLayoutingVisualTreeRoot = false;
+			}
 		}
 
-		/// <summary>
-		/// If the first arrange has been done since the control
-		/// is connected to its parent
-		/// </summary>
-		internal bool IsFirstArrangeDone
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private void DoMeasure(Size availableSize)
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.FirstArrangeDone);
-		}
+			var isFirstMeasure = !IsLayoutFlagSet(LayoutFlag.FirstMeasureDone);
+
+			var isDirty =
+				isFirstMeasure
+				|| (availableSize != m_previousAvailableSize)
+				|| IsMeasureDirty
+				|| !FeatureConfiguration.UIElement.UseInvalidateMeasurePath // dirty_path disabled globally
+				|| IsMeasureDirtyPathDisabled;
+
+			var isMeasureDirtyPath = IsMeasureDirtyPath;
 
+			if (!isDirty && !isMeasureDirtyPath)
+			{
+				return; // Nothing to do
+			}
+
+			if (isFirstMeasure)
+			{
+				SetLayoutFlags(LayoutFlag.FirstMeasureDone);
+			}
+
+			var remainingTries = MaxLayoutIterations;
+
+			while (--remainingTries > 0)
+			{
+				if (isDirty)
+				{
+					// We must reset the flag **BEFORE** doing the actual measure, so the elements are able to re-invalidate themselves
+					// TODO: This doesn't actually follow WinUI. It looks like in WinUI, the method
+					// CUIElement::MeasureInternal is doing SetIsMeasureDirty(FALSE); at the end.
+					// If we were able to align this to WinUI, we should remember clearing
+					// the flag in the Visibility == Visibility.Collapsed case as well.
+					// The Visibility condition is similar to the GetIsLayoutSuspended check in
+					// WinUI, which does goto Cleanup and will call SetIsMeasureDirty(FALSE);
+					ClearLayoutFlags(LayoutFlag.MeasureDirty | LayoutFlag.MeasureDirtyPath);
+
+					var prevSize = DesiredSize;
+
+					// The dirty flag is explicitly set on this element
+#if DEBUG
+					try
+#endif
+					{
+						if (this.Visibility == Visibility.Collapsed)
+						{
+							m_desiredSize = default;
+							RecursivelyApplyTemplateWorkaround();
+							return;
+						}
+
+						SetLayoutFlags(LayoutFlag.MeasuringSelf);
+						MeasureCore(availableSize);
+						ClearLayoutFlags(LayoutFlag.MeasuringSelf);
+						InvalidateArrange();
+					}
+#if DEBUG
+					catch (Exception ex)
+					{
+#if __CROSSRUNTIME__
+						_log.Error($"Error measuring {this}", ex);
 #else
-		/// <summary>
-		/// This is for compatibility - not implemented yet on this platform
-		/// </summary>
-		internal bool IsArrangeDirtyOrArrangeDirtyPath
+						_ = ex; // TODO
+#endif
+						throw;
+					}
+					finally
+#endif
+					{
+						m_previousAvailableSize = availableSize;
+
+						// if (!GetIsMeasureDuringArrange() && ! IsSameSize(prevSize, desiredSize) && !bInLayoutTransition)
+						if (!IsLayoutFlagSet(LayoutFlag.MeasureDuringArrange) && prevSize != DesiredSize)
+						{
+							if (GetUIElementAdjustedParentInternal() is { } pParent)
+							{
+								pParent.OnChildDesiredSizeChanged(pParent);
+							}
+						}
+					}
+
+					break;
+				}
+
+				// isMeasureDirtyPath is always true here
+				ClearLayoutFlags(LayoutFlag.MeasureDirtyPath);
+
+				// The dirty flag is set on one of the descendents:
+				// it will bypass the current element's MeasureOverride()
+				// since it shouldn't produce a different result and it's
+				// just a waste of precious CPU time to call it.
+				var children = this.GetChildren().GetEnumerator();
+
+				//foreach (var child in children)
+				while (children.MoveNext())
+				{
+					if (children.Current is UIElement { IsMeasureDirtyOrMeasureDirtyPath: true } child)
+					{
+						// If the child is dirty (or is a path to a dirty descendant child),
+						// We're remeasuring it.
+
+						var previousDesiredSize = child.DesiredSize;
+						child.EnsureLayoutStorage();
+						child.Measure(child.m_previousAvailableSize);
+						if (child.DesiredSize != previousDesiredSize)
+						{
+							isDirty = true;
+							break;
+						}
+					}
+				}
+
+				children.Dispose(); // no "using" operator here to prevent an implicit try-catch on Wasm
+
+				if (isDirty)
+				{
+					continue;
+				}
+
+				break;
+			}
+		}
+
+		internal virtual void MeasureCore(Size availableSize)
 		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsArrangeDirty || IsArrangeDirtyPath;
+			throw new NotSupportedException("UIElement doesn't implement MeasureCore. Inherit from FrameworkElement, which properly implements MeasureCore.");
 		}
-#endif
 
-		[Flags]
-		internal enum LayoutFlag : short
-		{
-			/// <summary>
-			/// Means the Measure is dirty for the current element
-			/// </summary>
-			MeasureDirty = 0b0000_0001,
-
-#if IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
-			/// <summary>
-			/// Means the Measure is dirty on at least one child of this element
-			/// </summary>
-			MeasureDirtyPath = 0b0000_0010,
-
-			/// <summary>
-			/// Indicates that the element is currently being measured during the Arrange cycle.
-			/// </summary>
-			MeasureDuringArrange = 0b0000_0001_0000_0000,
-
-			/// <summary>
-			/// Indicates that the element is currently being measured.
-			/// </summary>
-			MeasuringSelf = 0b0000_0010_0000_0000,
-#endif
+		internal bool ShouldApplyLayoutClipAsAncestorClip()
+		{
+			return this is Panel; // Restrict to Panels, to limit app-compat risk
+								  //&& !GetIsScrollViewerHeader(); // Special-case:  ScrollViewer Headers, which can zoom, must scale the LayoutClip too
+		}
 
-			/// <summary>
-			/// Indicates that first measure has been done on the element after been connected to parent
-			/// </summary>
-			FirstMeasureDone = 0b0000_0100,
-
-			/// <summary>
-			/// Means the MeasureDirtyPath is disabled on this element.
-			/// </summary>
-			/// <remarks>
-			/// This flag is copied to children when they are attached, but can be re-enabled afterwards.
-			/// This flag is used during invalidation
-			/// </remarks>
-			MeasureDirtyPathDisabled = 0b0000_1000,
-
-#if !__ANDROID__ // On Android, it's directly connected to IsLayoutRequested
-			/// <summary>
-			/// Means the Arrange is dirty on the current element or one of its child
-			/// </summary>
-			ArrangeDirty = 0b0001_0000,
-#endif
+		private void RecursivelyApplyTemplateWorkaround()
+		{
+			// Uno workaround. The template should NOT be applied here.
+			// But, without this workaround, VerifyVisibilityChangeUpdatesCommandBarVisualState test will fail.
+			// The real root cause for the test failure is that FindParentCommandBarForElement will
+			// return null, that is because Uno doesn't yet properly have a "logical parent" concept.
+			// We eagerly apply the template so that FindParentCommandBarForElement will
+			// find the command bar through TemplatedParent
+			if (this is Control thisAsControl)
+			{
+				thisAsControl.TryCallOnApplyTemplate();
 
-#if IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
-			/// <summary>
-			/// Means the Arrange is dirty on at least one child of this element
-			/// </summary>
-			ArrangeDirtyPath = 0b0010_0000,
-
-			/// <summary>
-			/// Indicates that first arrange has been done on the element and we can use the
-			/// LayoutInformation.GetLayoutSlot() to get previous finalRect
-			/// </summary>
-			FirstArrangeDone = 0b0100_0000,
-#endif
+				// Update bindings to ensure resources defined
+				// in visual parents get applied.
+				this.UpdateResourceBindings();
+			}
 
-			/// <summary>
-			/// Means the MeasureDirtyPath is disabled on this element.
-			/// </summary>
-			/// <remarks>
-			/// This flag is copied to children when they are attached, but can be re-enabled afterwards.
-			/// This flag is used during invalidation
-			/// </remarks>
-			ArrangeDirtyPathDisabled = 0b1000_0000,
-		}
-
-		private const LayoutFlag DEFAULT_STARTING_LAYOUTFLAGS = 0;
-		private const LayoutFlag LAYOUT_FLAGS_TO_CLEAR_ON_RESET =
-			LayoutFlag.MeasureDirty |
-#if IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
-			LayoutFlag.MeasureDirtyPath |
-			LayoutFlag.MeasureDuringArrange |
-			LayoutFlag.MeasuringSelf |
+#if __CROSSRUNTIME__
+			foreach (var child in _children)
+#else
+			foreach (var childView in this.GetChildren())
 #endif
-#if !__ANDROID__
-			LayoutFlag.ArrangeDirty |
+			{
+#if !__CROSSRUNTIME__
+				if (childView is UIElement child)
 #endif
-#if IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
-			LayoutFlag.ArrangeDirtyPath |
-			LayoutFlag.FirstArrangeDone |
+				{
+					child.RecursivelyApplyTemplateWorkaround();
+				}
+			}
+		}
+
+		public void Arrange(Rect finalRect)
+		{
+#if __CROSSRUNTIME__
+			if (!_isFrameworkElement)
+#else
+			if (this is not FrameworkElement)
 #endif
-			LayoutFlag.FirstMeasureDone;
+			{
+				return;
+			}
 
-		private LayoutFlag _layoutFlags = DEFAULT_STARTING_LAYOUTFLAGS;
+			var firstArrangeDone = IsFirstArrangeDone;
 
-		/// <summary>
-		/// Check for one specific layout flag
-		/// </summary>
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal bool IsLayoutFlagSet(LayoutFlag flag) => (_layoutFlags & flag) == flag;
+			if (Visibility == Visibility.Collapsed)
+			{
+				m_finalRect = finalRect;
+				HideVisual();
+				ClearLayoutFlags(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
+				return;
+			}
 
-		/// <summary>
-		/// Check that at least one of the specified flags is set
-		/// </summary>
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal bool IsAnyLayoutFlagsSet(LayoutFlag flags) => (_layoutFlags & flags) != 0;
+			if (firstArrangeDone && !IsArrangeDirtyOrArrangeDirtyPath && finalRect == m_finalRect)
+			{
+				ClearLayoutFlags(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
+				return; // Calling Arrange would be a waste of CPU time here.
+			}
 
-		/// <summary>
-		/// Set one or many flags (set to 1)
-		/// </summary>
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal void SetLayoutFlags(LayoutFlag flags) => _layoutFlags |= flags;
+			if (IsVisualTreeRoot)
+			{
+				ArrangeVisualTreeRoot(finalRect);
+			}
+			else
+			{
+				// If possible we avoid the try/finally which might be costly on some platforms
+				DoArrange(finalRect);
+			}
+		}
 
+		/// <remarks>
+		/// This method contains or is called by a try/catch containing method and can be significantly slower than other methods as a result on WebAssembly.
+		/// See https://github.com/dotnet/runtime/issues/56309
+		/// </remarks>
 		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal void SetLayoutFlags(LayoutFlag flags, bool state)
+		private void ArrangeVisualTreeRoot(Rect finalRect)
 		{
-			if (state)
+			try
 			{
-				SetLayoutFlags(flags);
+				_isLayoutingVisualTreeRoot = true;
+				DoArrange(finalRect);
 			}
-			else
+			finally
 			{
-				ClearLayoutFlags(flags);
+				_isLayoutingVisualTreeRoot = false;
 			}
 		}
 
-		/// <summary>
-		/// Reset one or many flags (set flag to zero)
-		/// </summary>
 		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal void ClearLayoutFlags(LayoutFlag flags) => _layoutFlags &= ~flags;
+		private void DoArrange(Rect finalRect)
+		{
+			var isFirstArrange = !IsLayoutFlagSet(LayoutFlag.FirstArrangeDone);
 
-		/// <summary>
-		/// Reset flags to original state
-		/// </summary>
-		[MethodImpl(MethodImplOptions.AggressiveInlining)]
-		internal void ResetLayoutFlags() => ClearLayoutFlags(LAYOUT_FLAGS_TO_CLEAR_ON_RESET);
+			var isDirty =
+				isFirstArrange
+				|| IsArrangeDirty
+				|| finalRect != m_finalRect;
 
-		internal bool IsMeasureDirtyPathDisabled
-		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.MeasureDirtyPathDisabled);
+			if (!isDirty && !IsArrangeDirtyPath)
+			{
+				return; // Nothing do to
+			}
+
+			if (GetUseLayoutRounding())
+			{
+				finalRect.X = LayoutRound(finalRect.X);
+				finalRect.Y = LayoutRound(finalRect.Y);
+				finalRect.Width = LayoutRound(finalRect.Width);
+				finalRect.Height = LayoutRound(finalRect.Height);
+			}
+
+			var remainingTries = MaxLayoutIterations;
+
+			while (--remainingTries > 0)
+			{
+				if (IsMeasureDirtyOrMeasureDirtyPath)
+				{
+					// Uno doc: in WinUI, the flag is only set and reset if IsMeasureDirty, not IsMeasureDirtyOrMeasureDirtyPath
+					SetLayoutFlags(LayoutFlag.MeasureDuringArrange);
+					DoMeasure(m_previousAvailableSize);
+					ClearLayoutFlags(LayoutFlag.MeasureDuringArrange);
+				}
+
+				if (isDirty)
+				{
+					ShowVisual();
+
+					// We must store the updated slot before natively arranging the element,
+					// so the updated value can be read by indirect code that is being invoked on arrange.
+					// For instance, the EffectiveViewPort computation reads that value to detect slot changes (cf. PropagateEffectiveViewportChange)
+					m_finalRect = finalRect;
+
+					// We must reset the flag **BEFORE** doing the actual arrange, so the elements are able to re-invalidate themselves
+					ClearLayoutFlags(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
+
+					ArrangeCore(finalRect);
+
+					SetLayoutFlags(LayoutFlag.FirstArrangeDone);
+
+					break;
+				}
+				else if (IsArrangeDirtyPath)
+				{
+					ClearLayoutFlags(LayoutFlag.ArrangeDirtyPath);
+
+					var children = this.GetChildren().GetEnumerator();
+
+					while (children.MoveNext())
+					{
+						var child = children.Current;
+
+						if (child is UIElement { IsArrangeDirtyOrArrangeDirtyPath: true } childAsUIElement)
+						{
+							var previousRenderSize = childAsUIElement.RenderSize;
+							childAsUIElement.Arrange(childAsUIElement.m_finalRect);
+
+							if (childAsUIElement.RenderSize != previousRenderSize)
+							{
+								isDirty = true;
+								break;
+							}
+						}
+					}
+
+					children.Dispose(); // no "using" operator here to prevent an implicit try-catch on Wasm
+
+					if (!isDirty)
+					{
+						break;
+					}
+				}
+				else
+				{
+					break;
+				}
+			}
 
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			set => SetLayoutFlags(LayoutFlag.MeasureDirtyPathDisabled, value);
 		}
 
-		internal bool IsArrangeDirtyPathDisabled
-		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => IsLayoutFlagSet(LayoutFlag.ArrangeDirtyPathDisabled);
+		partial void HideVisual();
+		partial void ShowVisual();
 
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			set => SetLayoutFlags(LayoutFlag.ArrangeDirtyPathDisabled, value);
+		internal virtual void ArrangeCore(Rect finalRect)
+		{
+			throw new NotSupportedException("UIElement doesn't implement ArrangeCore. Inherit from FrameworkElement, which properly implements ArrangeCore.");
 		}
 	}
 }
+#endif
diff --git a/src/Uno.UI/UI/Xaml/UIElement.cs b/src/Uno.UI/UI/Xaml/UIElement.cs
index aeb9fcab2327..3dd7de9e18a8 100644
--- a/src/Uno.UI/UI/Xaml/UIElement.cs
+++ b/src/Uno.UI/UI/Xaml/UIElement.cs
@@ -801,10 +801,12 @@ private static void InnerUpdateLayout(UIElement root)
 #elif __ANDROID__
 			for (var i = 0; i < MaxLayoutIterations; i++)
 			{
-				// On Android, Measure and arrange are the same
 				if (root.IsMeasureDirtyOrMeasureDirtyPath)
 				{
 					root.Measure(bounds.Size);
+				}
+				else if (root.IsArrangeDirtyOrArrangeDirtyPath)
+				{
 					root.Arrange(bounds);
 				}
 				else
@@ -933,35 +935,12 @@ internal void ApplyClip()
 
 #elif __WASM__
 			InvalidateArrange();
-#else
-			Rect rect;
-
-			if (Clip == null)
-			{
-				rect = Rect.Empty;
-
-				if (NeedsClipToSlot)
-				{
-#if UNO_REFERENCE_API
-					rect = new Rect(0, 0, RenderSize.Width, RenderSize.Height);
-#else
-					rect = ClippedFrame ?? Rect.Empty;
-#endif
-				}
-			}
-			else
-			{
-				rect = Clip.Rect;
-
-				// Apply transform to clipping mask, if any
-				if (Clip.Transform != null)
-				{
-					rect = Clip.Transform.TransformBounds(rect);
-				}
-			}
-
-			ApplyNativeClip(rect);
-			OnViewportUpdated(rect);
+#elif __ANDROID__
+			// TODO:
+#elif __IOS__
+			// TODO:
+#elif __MACOS__
+			// TODO:
 #endif
 		}
 
@@ -1078,102 +1057,6 @@ public Size RenderSize
 		}
 #endif
 
-
-#if !UNO_REFERENCE_API
-
-		/// <summary>
-		/// This is the Frame that should be used as "available Size" for the Arrange phase.
-		/// </summary>
-		internal Rect? ClippedFrame;
-
-		/// <summary>
-		/// Updates the DesiredSize of a UIElement. Typically, objects that implement custom layout for their
-		/// layout children call this method from their own MeasureOverride implementations to form a recursive layout update.
-		/// </summary>
-		/// <param name="availableSize">
-		/// The available space that a parent can allocate to a child object. A child object can request a larger
-		/// space than what is available; the provided size might be accommodated if scrolling or other resize behavior is
-		/// possible in that particular container.
-		/// </param>
-		/// <returns>The measured size.</returns>
-		/// <remarks>
-		/// Under Uno.UI, this method should not be called during the normal layouting phase. Instead, use the
-		/// <see cref="MeasureElement(View, Size)"/> methods, which handles native view properly.
-		/// </remarks>
-		public void Measure(Size availableSize)
-		{
-			EnsureLayoutStorage();
-
-			if (this is not FrameworkElement fwe)
-			{
-				return;
-			}
-
-			if (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height))
-			{
-				throw new InvalidOperationException($"Cannot measure [{GetType()}] with NaN");
-			}
-
-			((ILayouterElement)fwe).Layouter.Measure(availableSize);
-#if IS_UNIT_TESTS
-			OnMeasurePartial(availableSize);
-#endif
-		}
-
-#if IS_UNIT_TESTS
-		partial void OnMeasurePartial(Size slotSize);
-#endif
-
-		/// <summary>
-		/// Positions child objects and determines a size for a UIElement. Parent objects that implement custom layout
-		/// for their child elements should call this method from their layout override implementations to form a recursive layout update.
-		/// </summary>
-		/// <param name="finalRect">The final size that the parent computes for the child in layout, provided as a <see cref="Windows.Foundation.Rect"/> value.</param>
-		public void Arrange(Rect finalRect)
-		{
-			EnsureLayoutStorage();
-
-			if (this is not FrameworkElement fwe)
-			{
-				return;
-			}
-
-			var layouter = ((ILayouterElement)fwe).Layouter;
-			layouter.Arrange(finalRect.DeflateBy(fwe.Margin));
-			layouter.ArrangeChild(fwe, finalRect);
-		}
-
-		public void InvalidateMeasure()
-		{
-#if __ANDROID__
-			// Use a non-virtual version of the RequestLayout method, for performance.
-			base.RequestLayout();
-			SetLayoutFlags(LayoutFlag.MeasureDirty);
-#elif __IOS__
-			SetNeedsLayout();
-			SetLayoutFlags(LayoutFlag.MeasureDirty);
-#elif __MACOS__
-			base.NeedsLayout = true;
-			SetLayoutFlags(LayoutFlag.MeasureDirty);
-#endif
-
-			OnInvalidateMeasure();
-		}
-
-		protected internal virtual void OnInvalidateMeasure()
-		{
-		}
-
-		[global::Uno.NotImplemented]
-		public void InvalidateArrange()
-		{
-			InvalidateMeasure();
-#if __IOS__ || __MACOS__
-			SetLayoutFlags(LayoutFlag.ArrangeDirty);
-#endif
-		}
-#endif
-
 		/// <summary>
 		/// This method has to be invoked for elements that are going to be recycled WITHOUT necessarily being unloaded / loaded.
 		/// For instance, this is not expected to be invoked for elements recycled by the template pool as they are always unloaded.
diff --git a/src/Uno.UI/UI/Xaml/UIElement.iOS.cs b/src/Uno.UI/UI/Xaml/UIElement.iOS.cs
index 2377133ccfad..d2a34705270d 100644
--- a/src/Uno.UI/UI/Xaml/UIElement.iOS.cs
+++ b/src/Uno.UI/UI/Xaml/UIElement.iOS.cs
@@ -47,18 +47,6 @@ public override void MovedToWindow()
 			}
 		}
 
-		internal bool IsMeasureDirtyPath
-		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => false; // Not implemented on iOS yet
-		}
-
-		internal bool IsArrangeDirtyPath
-		{
-			[MethodImpl(MethodImplOptions.AggressiveInlining)]
-			get => false; // Not implemented on iOS yet
-		}
-
 		internal bool ClippingIsSetByCornerRadius { get; set; }
 
 		partial void ApplyNativeClip(Rect rect)
@@ -153,6 +141,12 @@ public void SetSubviewsNeedLayout()
 			}
 		}
 
+		internal void ArrangeVisual(Rect finalRect, Rect? clippedFrame = default)
+		{
+			// TODO: clipped frame?
+			this.Frame = ViewHelper.LogicalToPhysicalPixels(finalRect);
+		}
+
 		internal global::Windows.Foundation.Point GetPosition(Point position, global::Microsoft.UI.Xaml.UIElement relativeTo)
 		{
 			return ConvertPointToCoordinateSpace(position, relativeTo);