diff --git a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml
index e1762ab92d18..aeb74084b579 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml
+++ b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml
@@ -1,6 +1,18 @@
+ x:DataType="local:MainPage">
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs
index effdbcdb46d7..8ae3891a5f54 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs
+++ b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs
@@ -1,18 +1,70 @@
using System;
-using System.Collections.ObjectModel;
using System.Diagnostics;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Maui;
using Microsoft.Maui.Controls;
-using Microsoft.Maui.Graphics;
-namespace Maui.Controls.Sample
+namespace Maui.Controls.Sample;
+
+public partial class MainPage : ContentPage
{
- public partial class MainPage : ContentPage
+ const int rowCount = 30;
+ const int columnCount = 30;
+
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+
+ private void ClearGrid_Clicked(object sender, EventArgs e)
+ {
+ Stopwatch sw = Stopwatch.StartNew();
+
+ contentGrid.Clear();
+
+ sw.Stop();
+
+ info.Text = $"Clearing grid took: {sw.ElapsedMilliseconds} ms";
+ }
+
+ private void Button_Clicked(object sender, EventArgs e)
{
- public MainPage()
+ Stopwatch sw = Stopwatch.StartNew();
+ contentGrid.Clear();
+
+ for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
{
- InitializeComponent();
+ for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
+ {
+ Label label = new Label() { Text = $"[{columnIndex}x{rowIndex}]" };
+ contentGrid.Add(label, column: columnIndex, row: rowIndex);
+ }
}
+
+ sw.Stop();
+ info.Text = $"Clearing grid took: {sw.ElapsedMilliseconds} ms";
}
+
+ private void BatchGenerate_Clicked(object sender, EventArgs e)
+ {
+ Stopwatch sw = Stopwatch.StartNew();
+
+ int batchSize = int.Parse(BatchSize.Text);
+
+ for (int i = 0; i < batchSize; i++)
+ {
+ contentGrid.Clear();
+
+ for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
+ {
+ for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
+ {
+ Label label = new Label() { Text = $"[{columnIndex}x{rowIndex}]" };
+ contentGrid.Add(label, column: columnIndex, row: rowIndex);
+ }
+ }
+ }
+
+ sw.Stop();
+ info.Text = $"Grid was created {batchSize} times and it took {sw.ElapsedMilliseconds} ms in total. Avg run took {Math.Round(sw.ElapsedMilliseconds / (double)batchSize, 2)} ms";
+ }
+
}
\ No newline at end of file
diff --git a/src/Core/src/Handlers/View/ViewHandler.cs b/src/Core/src/Handlers/View/ViewHandler.cs
index eb3ac9b6eaaf..f4c7f60fe605 100644
--- a/src/Core/src/Handlers/View/ViewHandler.cs
+++ b/src/Core/src/Handlers/View/ViewHandler.cs
@@ -27,6 +27,9 @@ public abstract partial class ViewHandler : ElementHandler, IViewHandler
#if ANDROID
// Use a custom mapper for Android which knows how to batch the initial property sets
new AndroidBatchPropertyMapper(ElementMapper)
+#elif WINDOWS
+ // Use a custom mapper for Windows which knows how to batch the initial property sets
+ new WindowsBatchPropertyMapper(ElementMapper)
#else
new PropertyMapper(ElementHandler.ElementMapper)
#endif
diff --git a/src/Core/src/Handlers/View/WindowsBatchPropertyMapper.cs b/src/Core/src/Handlers/View/WindowsBatchPropertyMapper.cs
new file mode 100644
index 000000000000..1668373bfad5
--- /dev/null
+++ b/src/Core/src/Handlers/View/WindowsBatchPropertyMapper.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Maui.Handlers;
+
+#if WINDOWS
+class WindowsBatchPropertyMapper : PropertyMapper
+ where TVirtualView : IElement
+ where TViewHandler : IElementHandler
+{
+ // During mass property updates, this list of properties will be skipped
+ public static HashSet SkipList = new(StringComparer.Ordinal)
+ {
+ // TranslationX does the work that is necessary to make other properties work.
+ nameof(IView.TranslationY),
+ nameof(IView.Scale),
+ nameof(IView.ScaleX),
+ nameof(IView.ScaleY),
+ nameof(IView.Rotation),
+ nameof(IView.RotationX),
+ nameof(IView.RotationY),
+ nameof(IView.AnchorX),
+ nameof(IView.AnchorY),
+ };
+
+ public WindowsBatchPropertyMapper(params IPropertyMapper[] chained) : base(chained) { }
+
+ public override IEnumerable GetKeys()
+ {
+ foreach (var key in _mapper.Keys)
+ {
+ // When reporting the key list for mass updates up the chain, ignore properties in SkipList.
+ // These will be handled by ViewHandler.SetVirtualView() instead.
+ if (SkipList.Contains(key))
+ {
+ continue;
+ }
+
+ yield return key;
+ }
+
+ if (Chained is not null)
+ {
+ foreach (var chain in Chained)
+ foreach (var key in chain.GetKeys())
+ yield return key;
+ }
+ }
+}
+#endif
\ No newline at end of file