ScrollView content needs layout when ScrollView.InvalidateMeasure is called (#17639)
### Description of Change When `ScrollView.Content` changes its alignment (like `HorizontalOptions` change from `Start` to `End`), then the `ScrollView`'s layout needs to properly update. It worked on Android and Mac/iOS, but Windows would not update layout until the window was resized. After `HorizontalOptions` changed on the content, Windows would call `InvalidateMeasure` on the WinUI `ScrollView`, but the child content didn't get updated. This change makes sure that the content also gets measured. This fix is helpful for XAML Hot Reload, so the layout will update as `HorizontalOptions` changes. ### Issues Fixed Fixes #14377
This commit is contained in:
Коммит
34d73c8ee5
|
@ -60,6 +60,38 @@ namespace Microsoft.Maui.DeviceTests
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestContentHorizontalOptionsChanged()
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
BackgroundColor = Colors.LightBlue,
|
||||
HorizontalOptions = LayoutOptions.Start,
|
||||
Text = "Hello",
|
||||
WidthRequest = 50,
|
||||
};
|
||||
|
||||
var scrollView = new ScrollView
|
||||
{
|
||||
BackgroundColor = Colors.DarkBlue,
|
||||
Content = label,
|
||||
WidthRequest = 300,
|
||||
HeightRequest = 200,
|
||||
};
|
||||
|
||||
SetupBuilder();
|
||||
|
||||
await AttachAndRun(scrollView, async (handler) =>
|
||||
{
|
||||
// Without this delay, the UI didn't render and the bug didn't repro
|
||||
await Task.Delay(100);
|
||||
|
||||
await WaitAssert(() => CloseEnough(scrollView.Content.Frame.Left, 0.0));
|
||||
scrollView.Content.HorizontalOptions = LayoutOptions.End;
|
||||
await WaitAssert(() => CloseEnough(scrollView.Content.Frame.Right, 300.0));
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ScrollOrientation.Vertical, 100, 300, 0, 100)]
|
||||
[InlineData(ScrollOrientation.Horizontal, 0, 100, 100, 300)]
|
||||
|
@ -188,22 +220,17 @@ namespace Microsoft.Maui.DeviceTests
|
|||
|
||||
static async Task AssertContentSize(Func<Size> actual, Size expected)
|
||||
{
|
||||
await WaitAssert(() => CloseEnough(actual(), expected, 0.2), timeout: 5000, message: $"ContentSize was {actual()}, expected {expected}");
|
||||
await WaitAssert(() => CloseEnough(actual(), expected), timeout: 5000, message: $"ContentSize was {actual()}, expected {expected}");
|
||||
}
|
||||
|
||||
static bool CloseEnough(Size a, Size b, double tolerance)
|
||||
static bool CloseEnough(double a, double b, double tolerance = 0.2)
|
||||
{
|
||||
if (System.Math.Abs(a.Width - b.Width) > tolerance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return System.Math.Abs(a - b) <= tolerance;
|
||||
}
|
||||
|
||||
if (System.Math.Abs(a.Height - b.Height) > tolerance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
static bool CloseEnough(Size a, Size b, double tolerance = 0.2)
|
||||
{
|
||||
return CloseEnough(a.Width, b.Width, tolerance) && CloseEnough(a.Height, b.Height, tolerance);
|
||||
}
|
||||
|
||||
static Task<bool> WatchContentSizeChanged(ScrollView scrollView)
|
||||
|
|
|
@ -20,6 +20,16 @@ namespace Microsoft.Maui.Handlers
|
|||
return new ScrollViewer();
|
||||
}
|
||||
|
||||
internal static void MapInvalidateMeasure(IScrollViewHandler handler, IView view, object? args)
|
||||
{
|
||||
handler.PlatformView.InvalidateMeasure(view);
|
||||
|
||||
if (handler.PlatformView.Content is FrameworkElement content)
|
||||
{
|
||||
content.InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ConnectHandler(ScrollViewer platformView)
|
||||
{
|
||||
base.ConnectHandler(platformView);
|
||||
|
|
|
@ -29,7 +29,10 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static CommandMapper<IScrollView, IScrollViewHandler> CommandMapper = new(ViewCommandMapper)
|
||||
{
|
||||
[nameof(IScrollView.RequestScrollTo)] = MapRequestScrollTo
|
||||
[nameof(IScrollView.RequestScrollTo)] = MapRequestScrollTo,
|
||||
#if WINDOWS
|
||||
[nameof(IView.InvalidateMeasure)] = MapInvalidateMeasure,
|
||||
#endif
|
||||
};
|
||||
|
||||
public ScrollViewHandler() : base(Mapper, CommandMapper)
|
||||
|
|
Загрузка…
Ссылка в новой задаче