diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue15066.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue15066.cs
new file mode 100644
index 00000000000..1d9af42bc0c
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue15066.cs
@@ -0,0 +1,45 @@
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.Forms.Core.UITests;
+using NUnit.Framework;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+ [Category(UITestCategories.ManualReview)]
+#endif
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Github, 15066, "[Bug] StackLayout's layout inside ScrollView is not updated properly when adding children", PlatformAffected.Android)]
+ public class Issue15066 : TestContentPage
+ {
+ protected override void Init()
+ {
+ Button addButton = new Button()
+ {
+ Text = "Add",
+ };
+ Label testInstructionsLabel = new Label()
+ {
+ Text = "Click the 'Add' button until the height of the dark green StackLayout is larger than the height of the page. " +
+ "Then scroll to the end of the page. " +
+ "If there is a dark blue gap at the end of the page, the test has failed.",
+ HorizontalTextAlignment = TextAlignment.Center,
+ };
+ StackLayout containerStackLayout = new StackLayout()
+ {
+ BackgroundColor = Color.DarkGreen,
+ Children = { addButton, testInstructionsLabel },
+ };
+ addButton.Clicked += (_, __) =>
+ containerStackLayout.Children.Add(new StackLayout() { BackgroundColor = Color.Gray, HeightRequest = 40.0 });
+ Content = new ScrollView()
+ {
+ BackgroundColor = Color.DarkBlue,
+ Content = containerStackLayout,
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
index ff485b8a1ab..b1c6139a151 100644
--- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
@@ -1858,6 +1858,7 @@
+
diff --git a/Xamarin.Forms.Core/Device.cs b/Xamarin.Forms.Core/Device.cs
index b3363d084ff..46e26b68bb6 100644
--- a/Xamarin.Forms.Core/Device.cs
+++ b/Xamarin.Forms.Core/Device.cs
@@ -90,8 +90,6 @@ public static IPlatformServices PlatformServices
set { s_platformServices = value; }
}
- public static IPlatformInvalidate PlatformInvalidator { get; set; }
-
[EditorBrowsable(EditorBrowsableState.Never)]
public static IReadOnlyList Flags { get; private set; }
@@ -287,10 +285,5 @@ public static class Styles
public static readonly Style CaptionStyle = new Style(typeof(Label)) { BaseResourceKey = CaptionStyleKey };
}
-
- public static void Invalidate(VisualElement visualElement)
- {
- PlatformInvalidator?.Invalidate(visualElement);
- }
}
}
diff --git a/Xamarin.Forms.Core/IPlatformInvalidate.cs b/Xamarin.Forms.Core/IPlatformInvalidate.cs
deleted file mode 100644
index 012b21206cc..00000000000
--- a/Xamarin.Forms.Core/IPlatformInvalidate.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Xamarin.Forms.Internals
-{
- public interface IPlatformInvalidate
-
- {
- void Invalidate(VisualElement visualElement);
- }
-}
\ No newline at end of file
diff --git a/Xamarin.Forms.Core/Layout.cs b/Xamarin.Forms.Core/Layout.cs
index 98e0e29ed28..1e3f4974470 100644
--- a/Xamarin.Forms.Core/Layout.cs
+++ b/Xamarin.Forms.Core/Layout.cs
@@ -327,7 +327,7 @@ internal virtual void OnChildMeasureInvalidated(VisualElement child, Invalidatio
s_resolutionList.Add(new KeyValuePair(this, GetElementDepth(this)));
- if (Device.PlatformInvalidator == null && !s_relayoutInProgress)
+ if (!s_relayoutInProgress)
{
// Rather than recomputing the layout for each change as it happens, we accumulate them in
// s_resolutionList and schedule a single layout update operation to handle them all at once.
@@ -345,15 +345,9 @@ internal virtual void OnChildMeasureInvalidated(VisualElement child, Invalidatio
Device.BeginInvokeOnMainThread(ResolveLayoutChanges);
}
}
- else
- {
- // If the platform supports PlatformServices2, queueing is unnecessary; the layout changes
- // will be handled during the Layout's next Measure/Arrange pass
- Device.Invalidate(this);
- }
}
- public void ResolveLayoutChanges()
+ internal void ResolveLayoutChanges()
{
s_relayoutInProgress = false;
diff --git a/Xamarin.Forms.Core/VisualElement.cs b/Xamarin.Forms.Core/VisualElement.cs
index 3531c56df11..9b5a925ab6b 100644
--- a/Xamarin.Forms.Core/VisualElement.cs
+++ b/Xamarin.Forms.Core/VisualElement.cs
@@ -677,7 +677,6 @@ public void BatchCommit()
if (!Batched)
{
BatchCommitted?.Invoke(this, new EventArg(this));
- Device.Invalidate(this);
}
}
diff --git a/Xamarin.Forms.Platform.Android/Forms.cs b/Xamarin.Forms.Platform.Android/Forms.cs
index 7e31895aae2..2c2f2b90d51 100644
--- a/Xamarin.Forms.Platform.Android/Forms.cs
+++ b/Xamarin.Forms.Platform.Android/Forms.cs
@@ -308,10 +308,7 @@ static void SetupInit(
// because AndroidPlatformServices needs a current activity to launch URIs from
Profile.FramePartition("Device.PlatformServices");
- var androidServices = new AndroidPlatformServices(activity);
-
- Device.PlatformServices = androidServices;
- Device.PlatformInvalidator = androidServices;
+ Device.PlatformServices = new AndroidPlatformServices(activity);
// use field and not property to avoid exception in getter
if (Device.info != null)
@@ -645,7 +642,7 @@ protected override Expression VisitMember(MemberExpression node)
}
}
- class AndroidPlatformServices : IPlatformServices, IPlatformInvalidate
+ class AndroidPlatformServices : IPlatformServices
{
double _buttonDefaultSize;
double _editTextDefaultSize;
@@ -952,18 +949,6 @@ public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, dou
return Platform.Android.Platform.GetNativeSize(view, widthConstraint, heightConstraint);
}
- public void Invalidate(VisualElement visualElement)
- {
- var renderer = visualElement.GetRenderer();
- if (renderer == null || renderer.View.IsDisposed())
- {
- return;
- }
-
- renderer.View.Invalidate();
- renderer.View.RequestLayout();
- }
-
public OSAppTheme RequestedTheme
{
get
diff --git a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
index 0c422224571..ae9c18823c3 100644
--- a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
+++ b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
@@ -505,15 +505,5 @@ internal virtual void SendVisualElementInitialized(VisualElement element, AView
void IVisualElementRenderer.SetLabelFor(int? id)
=> ViewCompat.SetLabelFor(this, id ?? ViewCompat.GetLabelFor(this));
-
- protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
- {
- if (Element is Layout layout)
- {
- layout.ResolveLayoutChanges();
- }
-
- base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
- }
}
}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/VisualElementTracker.cs b/Xamarin.Forms.Platform.Android/VisualElementTracker.cs
index 21c99182dc3..93393ce4dba 100644
--- a/Xamarin.Forms.Platform.Android/VisualElementTracker.cs
+++ b/Xamarin.Forms.Platform.Android/VisualElementTracker.cs
@@ -336,9 +336,6 @@ void UpdateIsVisible()
aview.Visibility = ViewStates.Visible;
if (!view.IsVisible && aview.Visibility != ViewStates.Gone)
aview.Visibility = ViewStates.Gone;
-
- aview.Invalidate();
- aview.RequestLayout();
}
void UpdateNativeView(object sender, EventArgs e)
diff --git a/Xamarin.Forms.Platform.UAP/Forms.cs b/Xamarin.Forms.Platform.UAP/Forms.cs
index 6126ac239d1..7baee4694ad 100644
--- a/Xamarin.Forms.Platform.UAP/Forms.cs
+++ b/Xamarin.Forms.Platform.UAP/Forms.cs
@@ -58,11 +58,8 @@ public static void Init(IActivatedEventArgs launchActivatedEventArgs, IEnumerabl
Device.SetIdiom(TargetIdiom.Tablet);
Device.SetFlowDirection(GetFlowDirection());
- var platformServices = new WindowsPlatformServices(Window.Current.Dispatcher);
+ Device.PlatformServices = new WindowsPlatformServices(Window.Current.Dispatcher);
- Device.PlatformServices = platformServices;
- Device.PlatformInvalidator = platformServices;
-
Device.SetFlags(s_flags);
Device.Info = new WindowsDeviceInfo();
diff --git a/Xamarin.Forms.Platform.UAP/VisualElementRenderer.cs b/Xamarin.Forms.Platform.UAP/VisualElementRenderer.cs
index b017c5d591c..906861e972f 100644
--- a/Xamarin.Forms.Platform.UAP/VisualElementRenderer.cs
+++ b/Xamarin.Forms.Platform.UAP/VisualElementRenderer.cs
@@ -296,11 +296,6 @@ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Si
if (Element == null || availableSize.Width * availableSize.Height == 0)
return new Windows.Foundation.Size(0, 0);
- if (Element is Layout layout)
- {
- layout.ResolveLayoutChanges();
- }
-
Element.IsInNativeLayout = true;
for (var i = 0; i < ElementController.LogicalChildren.Count; i++)
diff --git a/Xamarin.Forms.Platform.UAP/WindowsBasePlatformServices.cs b/Xamarin.Forms.Platform.UAP/WindowsBasePlatformServices.cs
index b386509f89e..27bca80046a 100644
--- a/Xamarin.Forms.Platform.UAP/WindowsBasePlatformServices.cs
+++ b/Xamarin.Forms.Platform.UAP/WindowsBasePlatformServices.cs
@@ -25,7 +25,7 @@
namespace Xamarin.Forms.Platform.UWP
{
- internal abstract class WindowsBasePlatformServices : IPlatformServices, IPlatformInvalidate
+ internal abstract class WindowsBasePlatformServices : IPlatformServices
{
const string WrongThreadError = "RPC_E_WRONG_THREAD";
readonly CoreDispatcher _dispatcher;
@@ -256,17 +256,6 @@ await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
return await taskCompletionSource.Task;
}
- public void Invalidate(VisualElement visualElement)
- {
- var renderer = Platform.GetRenderer(visualElement);
- if (renderer == null)
- {
- return;
- }
-
- renderer.ContainerElement.InvalidateMeasure();
- }
-
public OSAppTheme RequestedTheme => Windows.UI.Xaml.Application.Current.RequestedTheme == ApplicationTheme.Dark ? OSAppTheme.Dark : OSAppTheme.Light;
}
}
diff --git a/Xamarin.Forms.Platform.iOS/Forms.cs b/Xamarin.Forms.Platform.iOS/Forms.cs
index 91c69b6f134..ff24ec68968 100644
--- a/Xamarin.Forms.Platform.iOS/Forms.cs
+++ b/Xamarin.Forms.Platform.iOS/Forms.cs
@@ -214,12 +214,9 @@ public static void Init()
}
#endif
Device.SetFlags(s_flags);
- var platformServices = new IOSPlatformServices();
-
- Device.PlatformServices = platformServices;
+ Device.PlatformServices = new IOSPlatformServices();
#if __MOBILE__
- Device.PlatformInvalidator = platformServices;
Device.Info = new IOSDeviceInfo();
#else
Device.Info = new Platform.macOS.MacDeviceInfo();
@@ -265,9 +262,6 @@ protected override Expression VisitMember(MemberExpression node)
}
class IOSPlatformServices : IPlatformServices
-#if __MOBILE__
- , IPlatformInvalidate
-#endif
{
readonly double _fontScalingFactor = 1;
public IOSPlatformServices()
@@ -824,18 +818,6 @@ static UIViewController GetCurrentViewController(bool throwIfNull = true)
return viewController;
}
-
- public void Invalidate(VisualElement visualElement)
- {
- var renderer = Platform.iOS.Platform.GetRenderer(visualElement);
-
- if (renderer == null)
- {
- return;
- }
-
- renderer.NativeView.SetNeedsLayout();
- }
#endif
}
}
diff --git a/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs b/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs
index 4d6ed225fb5..6760947ba17 100644
--- a/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs
+++ b/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs
@@ -313,24 +313,13 @@ public void SetElement(TElement element)
#if __MOBILE__
- void ResolveLayoutChanges()
- {
- if (Element is Layout layout)
- {
- layout.ResolveLayoutChanges();
- }
- }
-
public override SizeF SizeThatFits(SizeF size)
{
- ResolveLayoutChanges();
return new SizeF(0, 0);
}
public override void LayoutSubviews()
{
- ResolveLayoutChanges();
-
base.LayoutSubviews();
if (_blur != null && Superview != null)