From 2b5eb6c4db285f3bc07150e80f2df57fb86af978 Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" Date: Fri, 31 Jan 2020 14:15:18 -0800 Subject: [PATCH 001/105] Update dev/7.0.0 branch version number --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index ee65fed240d..a9ad31dbfc4 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "6.0.0-build.{height}", + "version": "7.0.0-build.{height}", "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/heads/dev$", // we release out of dev From 1250f2f2ec06a4c94b2ab0eced774306861ae17c Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 13:48:59 -0700 Subject: [PATCH 002/105] Add a new project for layout controls --- ...soft.Toolkit.Uwp.UI.Controls.Layout.csproj | 38 +++++++++++++++++++ ....Uwp.UI.Controls.Layout.csproj.DotSettings | 3 ++ .../Properties/AssemblyInfo.cs | 12 ++++++ ....Windows.Toolkit.UI.Controls.Layout.rd.xml | 5 +++ .../VisualStudioToolsManifest.xml | 6 +++ Windows Community Toolkit.sln | 33 ++++++++++++++++ 6 files changed, 97 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj.DotSettings create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/AssemblyInfo.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/Microsoft.Windows.Toolkit.UI.Controls.Layout.rd.xml create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/VisualStudioToolsManifest.xml diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj new file mode 100644 index 00000000000..0937fdb9d5a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj @@ -0,0 +1,38 @@ + + + + uap10.0.18362 + 10.0.18362.0 + Windows Community Toolkit Layout + + This library provides XAML layout controls. It is part of the Windows Community Toolkit. + + Microsoft.Toolkit.Uwp.UI.Controls + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj.DotSettings b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj.DotSettings new file mode 100644 index 00000000000..65848b5a192 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj.DotSettings @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..64a4e43ebc0 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Resources; +using System.Runtime.CompilerServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: InternalsVisibleTo("UnitTests")] +[assembly: NeutralResourcesLanguage("en-US")] \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/Microsoft.Windows.Toolkit.UI.Controls.Layout.rd.xml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/Microsoft.Windows.Toolkit.UI.Controls.Layout.rd.xml new file mode 100644 index 00000000000..342912dbe1c --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Properties/Microsoft.Windows.Toolkit.UI.Controls.Layout.rd.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/VisualStudioToolsManifest.xml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/VisualStudioToolsManifest.xml new file mode 100644 index 00000000000..8d2ad7fe8bf --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/VisualStudioToolsManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Windows Community Toolkit.sln b/Windows Community Toolkit.sln index 46e27005eba..b48bedc0a48 100644 --- a/Windows Community Toolkit.sln +++ b/Windows Community Toolkit.sln @@ -90,6 +90,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GazeInputTest", "GazeInputT EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Media", "Microsoft.Toolkit.Uwp.UI.Media\Microsoft.Toolkit.Uwp.UI.Media.csproj", "{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Layout", "Microsoft.Toolkit.Uwp.UI.Controls.Layout\Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj", "{CB444381-18BA-4A51-BB32-3A498BCC1E99}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution UnitTests\UnitTests.Notifications.Shared\UnitTests.Notifications.Shared.projitems*{982cc826-aacd-4855-9075-430bb6ce40a9}*SharedItemsImports = 13 @@ -801,6 +803,36 @@ Global {75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Release|x64.Build.0 = Release|Any CPU {75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Release|x86.ActiveCfg = Release|Any CPU {75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Release|x86.Build.0 = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|ARM.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|ARM64.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x64.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x64.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x86.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x86.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|Any CPU.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|Any CPU.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM64.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM64.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x64.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x64.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x86.ActiveCfg = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x86.Build.0 = Debug|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|Any CPU.Build.0 = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|ARM.ActiveCfg = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|ARM.Build.0 = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|ARM64.ActiveCfg = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|ARM64.Build.0 = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|x64.ActiveCfg = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|x64.Build.0 = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|x86.ActiveCfg = Release|Any CPU + {CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -824,6 +856,7 @@ Global {262BB7CE-EF42-4BF7-B90C-107E6CBB57FF} = {096ECFD7-7035-4487-9C87-81DCE9389620} {A122EA02-4DE7-413D-BFBF-AF7DFC668DD6} = {B30036C4-D514-4E5B-A323-587A061772CE} {75F9EE44-3EFA-47BC-AEDD-351B9834A0AF} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} + {CB444381-18BA-4A51-BB32-3A498BCC1E99} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5403B0C4-F244-4F73-A35C-FE664D0F4345} From ec3cca8796cdf6c8cfb23b0da7f4c834fcae707c Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 13:49:17 -0700 Subject: [PATCH 003/105] First port of the StaggeredPanel to the new StaggeredLayout --- ...soft.Toolkit.Uwp.UI.Controls.Layout.csproj | 7 - .../StaggeredLayout/StaggeredLayout.cs | 242 ++++++++++++++++++ 2 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj index 0937fdb9d5a..244df1c9e84 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj @@ -10,13 +10,6 @@ Microsoft.Toolkit.Uwp.UI.Controls - - - - - - - diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs new file mode 100644 index 00000000000..4e9bbe836fb --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// Arranges child elements into a staggered grid pattern where items are added to the column that has used least amount of space. + /// + public class StaggeredLayout : VirtualizingLayout + { + private double _columnWidth; + + /// + /// Initializes a new instance of the class. + /// + public StaggeredLayout() + { + } + + /// + /// Gets or sets the desired width for each column. + /// + /// + /// The width of columns can exceed the DesiredColumnWidth if the HorizontalAlignment is set to Stretch. + /// + public double DesiredColumnWidth + { + get { return (double)GetValue(DesiredColumnWidthProperty); } + set { SetValue(DesiredColumnWidthProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DesiredColumnWidthProperty = DependencyProperty.Register( + nameof(DesiredColumnWidth), + typeof(double), + typeof(StaggeredLayout), + new PropertyMetadata(250d, OnDesiredColumnWidthChanged)); + + /// + /// Gets or sets the distance between the border and its child object. + /// + /// + /// The dimensions of the space between the border and its child as a Thickness value. + /// Thickness is a structure that stores dimension values using pixel measures. + /// + public Thickness Padding + { + get { return (Thickness)GetValue(PaddingProperty); } + set { SetValue(PaddingProperty, value); } + } + + /// + /// Identifies the Padding dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register( + nameof(Padding), + typeof(Thickness), + typeof(StaggeredLayout), + new PropertyMetadata(default(Thickness), OnPaddingChanged)); + + /// + /// Gets or sets the spacing between columns of items. + /// + public double ColumnSpacing + { + get { return (double)GetValue(ColumnSpacingProperty); } + set { SetValue(ColumnSpacingProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ColumnSpacingProperty = DependencyProperty.Register( + nameof(ColumnSpacing), + typeof(double), + typeof(StaggeredLayout), + new PropertyMetadata(0d, OnPaddingChanged)); + + /// + /// Gets or sets the spacing between rows of items. + /// + public double RowSpacing + { + get { return (double)GetValue(RowSpacingProperty); } + set { SetValue(RowSpacingProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register( + nameof(RowSpacing), + typeof(double), + typeof(StaggeredLayout), + new PropertyMetadata(0d, OnPaddingChanged)); + + /// + protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) + { + double availableWidth = availableSize.Width - Padding.Left - Padding.Right; + double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; + + _columnWidth = Math.Min(DesiredColumnWidth, availableWidth); + int numColumns = Math.Max(1, (int)Math.Floor(availableWidth / _columnWidth)); + + // adjust for column spacing on all columns expect the first + double totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing)); + if (totalWidth > availableWidth) + { + numColumns--; + } + else if (double.IsInfinity(availableWidth)) + { + availableWidth = totalWidth; + } + + //if (HorizontalAlignment == HorizontalAlignment.Stretch) + //{ + // availableWidth = availableWidth - ((numColumns - 1) * ColumnSpacing); + // _columnWidth = availableWidth / numColumns; + //} + + if (context.ItemCount == 0) + { + return new Size(0, 0); + } + + var columnHeights = new double[numColumns]; + var itemsPerColumn = new double[numColumns]; + + for (int i = 0; i < context.ItemCount; i++) + { + var columnIndex = GetColumnIndex(columnHeights); + + var child = context.GetOrCreateElementAt(i); + child.Measure(new Size(_columnWidth, availableHeight)); + var elementSize = child.DesiredSize; + columnHeights[columnIndex] += elementSize.Height + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0); + itemsPerColumn[columnIndex]++; + } + + double desiredHeight = columnHeights.Max(); + + return new Size(availableWidth, desiredHeight); + } + + /// + protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) + { + double horizontalOffset = Padding.Left; + double verticalOffset = Padding.Top; + int numColumns = Math.Max(1, (int)Math.Floor(finalSize.Width / _columnWidth)); + + // adjust for horizontal spacing on all columns expect the first + double totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing)); + if (totalWidth > finalSize.Width) + { + numColumns--; + + // Need to recalculate the totalWidth for a correct horizontal offset + totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing)); + } + + //if (HorizontalAlignment == HorizontalAlignment.Right) + //{ + // horizontalOffset += finalSize.Width - totalWidth; + //} + //else if (HorizontalAlignment == HorizontalAlignment.Center) + //{ + // horizontalOffset += (finalSize.Width - totalWidth) / 2; + //} + + var columnHeights = new double[numColumns]; + var itemsPerColumn = new double[numColumns]; + + for (int i = 0; i < context.ItemCount; i++) + { + var columnIndex = GetColumnIndex(columnHeights); + + var child = context.GetOrCreateElementAt(i); + var elementSize = child.DesiredSize; + + double elementHeight = elementSize.Height; + + double itemHorizontalOffset = horizontalOffset + (_columnWidth * columnIndex) + (ColumnSpacing * columnIndex); + double itemVerticalOffset = columnHeights[columnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[columnIndex]); + + Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, _columnWidth, elementHeight); + child.Arrange(bounds); + + columnHeights[columnIndex] += elementSize.Height; + itemsPerColumn[columnIndex]++; + } + + return base.Arrange(context, finalSize); + } + + private static void OnDesiredColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var panel = (StaggeredLayout)d; + panel.InvalidateMeasure(); + } + + private static void OnPaddingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var panel = (StaggeredLayout)d; + panel.InvalidateMeasure(); + } + + private void OnHorizontalAlignmentChanged(DependencyObject sender, DependencyProperty dp) + { + InvalidateMeasure(); + } + + private int GetColumnIndex(double[] columnHeights) + { + int columnIndex = 0; + double height = columnHeights[0]; + for (int j = 1; j < columnHeights.Length; j++) + { + if (columnHeights[j] < height) + { + columnIndex = j; + height = columnHeights[j]; + } + } + + return columnIndex; + } + } +} From 04677f4d731e889088e0705a29ecd5291848c976 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 13:53:26 -0700 Subject: [PATCH 004/105] Create a new class that will hold layout information --- .../StaggeredLayout/StaggeredLayout.cs | 7 +++++++ .../StaggeredLayout/StaggeredLayoutState.cs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 4e9bbe836fb..e5b97e4af76 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -105,6 +105,13 @@ public double RowSpacing typeof(StaggeredLayout), new PropertyMetadata(0d, OnPaddingChanged)); + /// + protected override void InitializeForContextCore(VirtualizingLayoutContext context) + { + context.LayoutState = new StaggeredLayoutState(); + base.InitializeForContextCore(context); + } + /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs new file mode 100644 index 00000000000..a268e4051db --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal class StaggeredLayoutState + { + } +} From 7b6c797cefc5659af5391de3ac1d9093b4aa127a Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 13:55:01 -0700 Subject: [PATCH 005/105] Early exit if we don't have any items --- .../StaggeredLayout/StaggeredLayout.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index e5b97e4af76..b79525ab17e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -115,6 +115,11 @@ protected override void InitializeForContextCore(VirtualizingLayoutContext conte /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { + if (context.ItemCount == 0) + { + return new Size(availableSize.Width, 0); + } + double availableWidth = availableSize.Width - Padding.Left - Padding.Right; double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; @@ -138,11 +143,6 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // _columnWidth = availableWidth / numColumns; //} - if (context.ItemCount == 0) - { - return new Size(0, 0); - } - var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; From e42a770cd66156e54d453ce6864cb57fb57debe5 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 13:55:47 -0700 Subject: [PATCH 006/105] Early exit for no realization area to render to --- .../StaggeredLayout/StaggeredLayout.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index b79525ab17e..97d8d4be0bf 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -119,6 +119,10 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { return new Size(availableSize.Width, 0); } + if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0)) + { + return new Size(availableSize.Width, 0.0); + } double availableWidth = availableSize.Width - Padding.Left - Padding.Right; double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; From 5f8de428f5f8fa3b25d5ecf1710cdf193e5ea067 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 14:03:59 -0700 Subject: [PATCH 007/105] Don't work with the UIElement directly within the measure method. Instead use a new StaggeredItem to track the size of the element --- .../StaggeredLayout/StaggeredItem.cs | 42 +++++++++++++++++++ .../StaggeredLayout/StaggeredLayout.cs | 11 +++-- .../StaggeredLayout/StaggeredLayoutState.cs | 28 +++++++++++-- 3 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs new file mode 100644 index 00000000000..b8244fa95c4 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal class StaggeredItem + { + private VirtualizingLayoutContext _context; + private int _index; + private Size? _size; + + public StaggeredItem(VirtualizingLayoutContext context, int index) + { + _context = context; + this._index = index; + } + + internal Size Measure(double columnWidth, double availableHeight) + { + if (_size == null) + { + UIElement element = GetElement(); + + element.Measure(new Size(columnWidth, availableHeight)); + _size = element.DesiredSize; + } + + return _size.Value; + } + + private UIElement GetElement() + { + return _context.GetOrCreateElementAt(_index); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 97d8d4be0bf..a079a64219a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -108,7 +108,7 @@ public double RowSpacing /// protected override void InitializeForContextCore(VirtualizingLayoutContext context) { - context.LayoutState = new StaggeredLayoutState(); + context.LayoutState = new StaggeredLayoutState(context); base.InitializeForContextCore(context); } @@ -119,11 +119,14 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { return new Size(availableSize.Width, 0); } + if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0)) { return new Size(availableSize.Width, 0.0); } + var state = (StaggeredLayoutState)context.LayoutState; + double availableWidth = availableSize.Width - Padding.Left - Padding.Right; double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; @@ -154,9 +157,9 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { var columnIndex = GetColumnIndex(columnHeights); - var child = context.GetOrCreateElementAt(i); - child.Measure(new Size(_columnWidth, availableHeight)); - var elementSize = child.DesiredSize; + StaggeredItem item = state.GetItemAt(i); + Size elementSize = item.Measure(_columnWidth, availableHeight); + columnHeights[columnIndex] += elementSize.Height + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0); itemsPerColumn[columnIndex]++; } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index a268e4051db..567ed256cc0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -4,13 +4,35 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { internal class StaggeredLayoutState { + private List _items = new List(); + private VirtualizingLayoutContext _context; + + public StaggeredLayoutState(VirtualizingLayoutContext context) + { + _context = context; + } + + internal StaggeredItem GetItemAt(int index) + { + if (index < 0) throw new IndexOutOfRangeException(); + + if (index <= (_items.Count - 1)) + { + return _items[index]; + } + else + { + StaggeredItem item = new StaggeredItem(_context, index); + _items.Add(item); + return item; + } + } + } } From 3a1a7147f1c3d64156b65ffe225c465a08461340 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 15:34:36 -0700 Subject: [PATCH 008/105] Stop measuring elements when we are outside the realization rect --- .../StaggeredLayout/StaggeredLayout.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index a079a64219a..0a5b8703766 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -160,8 +160,15 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size StaggeredItem item = state.GetItemAt(i); Size elementSize = item.Measure(_columnWidth, availableHeight); + double top = columnHeights[columnIndex]; columnHeights[columnIndex] += elementSize.Height + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0); itemsPerColumn[columnIndex]++; + + if (top > context.RealizationRect.Bottom) + { + // The top of the element is below the realization area + break; + } } double desiredHeight = columnHeights.Max(); From 225a24fa52d5b0e450320a36efa93f9962f1e1ef Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 15:37:25 -0700 Subject: [PATCH 009/105] Track the top of the element on the StaggeredItem --- .../StaggeredLayout/StaggeredItem.cs | 2 ++ .../StaggeredLayout/StaggeredLayout.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs index b8244fa95c4..a9b36a4a6c4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -21,6 +21,8 @@ public StaggeredItem(VirtualizingLayoutContext context, int index) this._index = index; } + public double Top { get; internal set; } + internal Size Measure(double columnWidth, double availableHeight) { if (_size == null) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 0a5b8703766..a9f99227561 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -160,11 +160,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size StaggeredItem item = state.GetItemAt(i); Size elementSize = item.Measure(_columnWidth, availableHeight); - double top = columnHeights[columnIndex]; + item.Top = columnHeights[columnIndex]; columnHeights[columnIndex] += elementSize.Height + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0); itemsPerColumn[columnIndex]++; - if (top > context.RealizationRect.Bottom) + if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area break; From 98c3be0e96367a8b81191b159457de3620bcca39 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 15:51:47 -0700 Subject: [PATCH 010/105] Track the column layout and use that to arrange the items after measure --- .../StaggeredLayout/StaggeredColumnLayout.cs | 12 +++++++ .../StaggeredLayout/StaggeredItem.cs | 9 +++++ .../StaggeredLayout/StaggeredLayout.cs | 34 +++++++++++++------ .../StaggeredLayout/StaggeredLayoutState.cs | 18 ++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs new file mode 100644 index 00000000000..9c63ab97b9e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal class StaggeredColumnLayout : List + { + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs index a9b36a4a6c4..85556633606 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -23,6 +23,8 @@ public StaggeredItem(VirtualizingLayoutContext context, int index) public double Top { get; internal set; } + public double Height { get; internal set; } + internal Size Measure(double columnWidth, double availableHeight) { if (_size == null) @@ -31,6 +33,7 @@ internal Size Measure(double columnWidth, double availableHeight) element.Measure(new Size(columnWidth, availableHeight)); _size = element.DesiredSize; + Height = _size.Value.Height; } return _size.Value; @@ -40,5 +43,11 @@ private UIElement GetElement() { return _context.GetOrCreateElementAt(_index); } + + internal void Arrange(Rect bounds) + { + UIElement element = GetElement(); + element.Arrange(bounds); + } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index a9f99227561..245d59a15d6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -163,6 +163,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.Top = columnHeights[columnIndex]; columnHeights[columnIndex] += elementSize.Height + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0); itemsPerColumn[columnIndex]++; + state.AddItemToColumn(item, columnIndex); if (item.Top > context.RealizationRect.Bottom) { @@ -205,23 +206,34 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; - for (int i = 0; i < context.ItemCount; i++) + var state = (StaggeredLayoutState)context.LayoutState; + for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) { - var columnIndex = GetColumnIndex(columnHeights); + double top = verticalOffset; + StaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); + for (int i = 0; i < layout.Count; i++) + { + StaggeredItem item = state.GetItemAt(i); - var child = context.GetOrCreateElementAt(i); - var elementSize = child.DesiredSize; + if ((item.Top + item.Height) < context.RealizationRect.Top) + { + // element is above the realization bounds + continue; + } - double elementHeight = elementSize.Height; + if (item.Top <= context.RealizationRect.Bottom) + { - double itemHorizontalOffset = horizontalOffset + (_columnWidth * columnIndex) + (ColumnSpacing * columnIndex); - double itemVerticalOffset = columnHeights[columnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[columnIndex]); + double itemHorizontalOffset = horizontalOffset + (_columnWidth * columnIndex) + (ColumnSpacing * columnIndex); + double itemVerticalOffset = columnHeights[columnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[columnIndex]); - Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, _columnWidth, elementHeight); - child.Arrange(bounds); + Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, _columnWidth, item.Height); + item.Arrange(bounds); - columnHeights[columnIndex] += elementSize.Height; - itemsPerColumn[columnIndex]++; + columnHeights[columnIndex] += item.Height; + itemsPerColumn[columnIndex]++; + } + } } return base.Arrange(context, finalSize); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 567ed256cc0..58832e313f7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -12,12 +12,26 @@ internal class StaggeredLayoutState { private List _items = new List(); private VirtualizingLayoutContext _context; + private Dictionary _columnLayout = new Dictionary(); public StaggeredLayoutState(VirtualizingLayoutContext context) { _context = context; } + internal void AddItemToColumn(StaggeredItem item, int columnIndex) + { + if (_columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout) == false) + { + columnLayout = new StaggeredColumnLayout(); + _columnLayout[columnIndex] = columnLayout; + } + if (columnLayout.Contains(item) == false) + { + columnLayout.Add(item); + } + } + internal StaggeredItem GetItemAt(int index) { if (index < 0) throw new IndexOutOfRangeException(); @@ -34,5 +48,9 @@ internal StaggeredItem GetItemAt(int index) } } + internal StaggeredColumnLayout GetColumnLayout(int columnIndex) + { + throw new NotImplementedException(); + } } } From 78673db6b55bfe4c4e38f1d564018046e2463845 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 15:55:49 -0700 Subject: [PATCH 011/105] Make sure the top of the elemet accounts for the spacing between items --- .../StaggeredLayout/StaggeredLayout.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 245d59a15d6..fdd3d24e93c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -160,8 +160,9 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size StaggeredItem item = state.GetItemAt(i); Size elementSize = item.Measure(_columnWidth, availableHeight); - item.Top = columnHeights[columnIndex]; - columnHeights[columnIndex] += elementSize.Height + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0); + double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; + item.Top = columnHeights[columnIndex] + spacing; + columnHeights[columnIndex] = item.Top + item.Height; itemsPerColumn[columnIndex]++; state.AddItemToColumn(item, columnIndex); From 3bddce4d0dd23b0199de5818dd396fff81b28eff Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:00:22 -0700 Subject: [PATCH 012/105] Move the column width to the state --- .../StaggeredLayout/StaggeredLayout.cs | 25 +++++++++---------- .../StaggeredLayout/StaggeredLayoutState.cs | 2 ++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index fdd3d24e93c..1b583eca183 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -15,8 +15,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// public class StaggeredLayout : VirtualizingLayout { - private double _columnWidth; - /// /// Initializes a new instance of the class. /// @@ -130,11 +128,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double availableWidth = availableSize.Width - Padding.Left - Padding.Right; double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; - _columnWidth = Math.Min(DesiredColumnWidth, availableWidth); - int numColumns = Math.Max(1, (int)Math.Floor(availableWidth / _columnWidth)); + state.ColumnWidth = Math.Min(DesiredColumnWidth, availableWidth); + int numColumns = Math.Max(1, (int)Math.Floor(availableWidth / state.ColumnWidth)); // adjust for column spacing on all columns expect the first - double totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing)); + double totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); if (totalWidth > availableWidth) { numColumns--; @@ -147,7 +145,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size //if (HorizontalAlignment == HorizontalAlignment.Stretch) //{ // availableWidth = availableWidth - ((numColumns - 1) * ColumnSpacing); - // _columnWidth = availableWidth / numColumns; + // state.ColumnWidth = availableWidth / numColumns; //} var columnHeights = new double[numColumns]; @@ -158,7 +156,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var columnIndex = GetColumnIndex(columnHeights); StaggeredItem item = state.GetItemAt(i); - Size elementSize = item.Measure(_columnWidth, availableHeight); + Size elementSize = item.Measure(state.ColumnWidth, availableHeight); double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; item.Top = columnHeights[columnIndex] + spacing; @@ -181,18 +179,20 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size /// protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) { + var state = (StaggeredLayoutState)context.LayoutState; + double horizontalOffset = Padding.Left; double verticalOffset = Padding.Top; - int numColumns = Math.Max(1, (int)Math.Floor(finalSize.Width / _columnWidth)); + int numColumns = Math.Max(1, (int)Math.Floor(finalSize.Width / state.ColumnWidth)); // adjust for horizontal spacing on all columns expect the first - double totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing)); + double totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); if (totalWidth > finalSize.Width) { numColumns--; // Need to recalculate the totalWidth for a correct horizontal offset - totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing)); + totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); } //if (HorizontalAlignment == HorizontalAlignment.Right) @@ -207,7 +207,6 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; - var state = (StaggeredLayoutState)context.LayoutState; for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) { double top = verticalOffset; @@ -225,10 +224,10 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size if (item.Top <= context.RealizationRect.Bottom) { - double itemHorizontalOffset = horizontalOffset + (_columnWidth * columnIndex) + (ColumnSpacing * columnIndex); + double itemHorizontalOffset = horizontalOffset + (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); double itemVerticalOffset = columnHeights[columnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[columnIndex]); - Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, _columnWidth, item.Height); + Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, state.ColumnWidth, item.Height); item.Arrange(bounds); columnHeights[columnIndex] += item.Height; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 58832e313f7..fb03cb94ef7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -19,6 +19,8 @@ public StaggeredLayoutState(VirtualizingLayoutContext context) _context = context; } + public double ColumnWidth { get; internal set; } + internal void AddItemToColumn(StaggeredItem item, int columnIndex) { if (_columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout) == false) From 5615681418deeb1d4bff8584c9a56b7c80a5cfaa Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:03:47 -0700 Subject: [PATCH 013/105] Remove horizontal alignment calculations. I don't believe this should be part of a layout. The ItemsRepeater will handle that --- .../StaggeredLayout/StaggeredLayout.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 1b583eca183..83130b90e21 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -142,12 +142,6 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableWidth = totalWidth; } - //if (HorizontalAlignment == HorizontalAlignment.Stretch) - //{ - // availableWidth = availableWidth - ((numColumns - 1) * ColumnSpacing); - // state.ColumnWidth = availableWidth / numColumns; - //} - var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; @@ -195,15 +189,6 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); } - //if (HorizontalAlignment == HorizontalAlignment.Right) - //{ - // horizontalOffset += finalSize.Width - totalWidth; - //} - //else if (HorizontalAlignment == HorizontalAlignment.Center) - //{ - // horizontalOffset += (finalSize.Width - totalWidth) / 2; - //} - var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; From 57a1e6008ffaa16de51fe9a2c8555147e6c3f25a Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:11:53 -0700 Subject: [PATCH 014/105] No need to arrange anything if there is no realization bounds --- .../StaggeredLayout/StaggeredLayout.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 83130b90e21..5bd0890d07b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -173,6 +173,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size /// protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) { + if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0)) + { + return finalSize; + } + var state = (StaggeredLayoutState)context.LayoutState; double horizontalOffset = Padding.Left; From d2ee3f5259f80ed76fa472b233d7a4cc1b68036f Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:15:22 -0700 Subject: [PATCH 015/105] No need to keep arranging if we are outside the bounds --- .../StaggeredLayout/StaggeredLayout.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 5bd0890d07b..47ab31f60d6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -213,7 +213,6 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size if (item.Top <= context.RealizationRect.Bottom) { - double itemHorizontalOffset = horizontalOffset + (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); double itemVerticalOffset = columnHeights[columnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[columnIndex]); @@ -223,6 +222,10 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size columnHeights[columnIndex] += item.Height; itemsPerColumn[columnIndex]++; } + else + { + break; + } } } From 3814bcbf9d5000b3d7de11da4e9985bed8e8014e Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:15:42 -0700 Subject: [PATCH 016/105] Return the column for the given index --- .../StaggeredLayout/StaggeredLayoutState.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index fb03cb94ef7..b70dbc43853 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -52,7 +52,8 @@ internal StaggeredItem GetItemAt(int index) internal StaggeredColumnLayout GetColumnLayout(int columnIndex) { - throw new NotImplementedException(); + _columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout); + return columnLayout; } } } From b6a25e5e034cfb142b834194fae40e75a576b5a3 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:15:51 -0700 Subject: [PATCH 017/105] Return the final size always --- .../StaggeredLayout/StaggeredLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 47ab31f60d6..9401c5f8b34 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -229,7 +229,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size } } - return base.Arrange(context, finalSize); + return finalSize; } private static void OnDesiredColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) From 9eed9df94a2d4fa8127e0c51800e2fcb4882e78c Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:22:20 -0700 Subject: [PATCH 018/105] Get the item in the column layout, not the index --- .../StaggeredLayout/StaggeredLayout.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 9401c5f8b34..4c9d97c3f51 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -203,8 +203,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size StaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); for (int i = 0; i < layout.Count; i++) { - StaggeredItem item = state.GetItemAt(i); - + StaggeredItem item = layout[i]; if ((item.Top + item.Height) < context.RealizationRect.Top) { // element is above the realization bounds From becb6e4fea31af6e7c4edf259cdaacc2b10b0b8e Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:30:32 -0700 Subject: [PATCH 019/105] Stop measuring after we have measured all columns that fit the bounds --- .../StaggeredLayout/StaggeredLayout.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 4c9d97c3f51..4ef4ebb1a9c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Linq; using Microsoft.UI.Xaml.Controls; using Windows.Foundation; @@ -144,6 +145,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; + var deadColumns = new HashSet(); for (int i = 0; i < context.ItemCount; i++) { @@ -161,6 +163,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area + deadColumns.Add(columnIndex); + } + + if (deadColumns.Count == numColumns) + { break; } } From d8b1c561d02a30720c2d7ded0c3ac0fd16b92ec6 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:39:52 -0700 Subject: [PATCH 020/105] If the column width is different from what was previously there then clear the items because we will need to recalculate the size of the items --- .../StaggeredLayout/StaggeredLayout.cs | 6 ++++++ .../StaggeredLayout/StaggeredLayoutState.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 4ef4ebb1a9c..c6dc47ab6d6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -129,6 +129,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double availableWidth = availableSize.Width - Padding.Left - Padding.Right; double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; + double columnWidth = Math.Min(DesiredColumnWidth, availableWidth); + if (columnWidth != state.ColumnWidth) + { + state.ClearColumns(); + } + state.ColumnWidth = Math.Min(DesiredColumnWidth, availableWidth); int numColumns = Math.Max(1, (int)Math.Floor(availableWidth / state.ColumnWidth)); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index b70dbc43853..c50b04e59b2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -55,5 +55,11 @@ internal StaggeredColumnLayout GetColumnLayout(int columnIndex) _columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout); return columnLayout; } + + internal void ClearColumns() + { + _columnLayout.Clear(); + _items.Clear(); + } } } From c4424318bef19a026a81df3741bda2063fe14a5e Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 16:43:12 -0700 Subject: [PATCH 021/105] Manually recycle elements that fall outside of the bounds --- .../StaggeredLayout/StaggeredItem.cs | 6 ++++++ .../StaggeredLayout/StaggeredLayout.cs | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs index 85556633606..acb54e8a1b8 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -49,5 +49,11 @@ internal void Arrange(Rect bounds) UIElement element = GetElement(); element.Arrange(bounds); } + + internal void RecycleElement() + { + UIElement element = GetElement(); + _context.RecycleElement(element); + } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index c6dc47ab6d6..52db98b707f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -162,13 +162,20 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; item.Top = columnHeights[columnIndex] + spacing; - columnHeights[columnIndex] = item.Top + item.Height; + double bottom = item.Top + elementSize.Height; + columnHeights[columnIndex] = bottom; itemsPerColumn[columnIndex]++; state.AddItemToColumn(item, columnIndex); - if (item.Top > context.RealizationRect.Bottom) + if (bottom < context.RealizationRect.Top) + { + // The bottom of the element is above the realization area + item.RecycleElement(); + } + else if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area + item.RecycleElement(); deadColumns.Add(columnIndex); } From a6c3375b2b6dcbff8e9c8065247243d5b52d65eb Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:01:25 -0700 Subject: [PATCH 022/105] since we are staying within one column we do not need to track the height and number of items in each column --- .../StaggeredLayout/StaggeredLayout.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 52db98b707f..0982e66998e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -214,9 +214,6 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); } - var columnHeights = new double[numColumns]; - var itemsPerColumn = new double[numColumns]; - for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) { double top = verticalOffset; @@ -224,22 +221,26 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size for (int i = 0; i < layout.Count; i++) { StaggeredItem item = layout[i]; - if ((item.Top + item.Height) < context.RealizationRect.Top) + if (i > 0) { + top += RowSpacing; + } + + double bottom = top + item.Height; + if (bottom < context.RealizationRect.Top) + { + top += item.Height; // element is above the realization bounds continue; } - if (item.Top <= context.RealizationRect.Bottom) + if (top <= context.RealizationRect.Bottom) { double itemHorizontalOffset = horizontalOffset + (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); - double itemVerticalOffset = columnHeights[columnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[columnIndex]); - Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, state.ColumnWidth, item.Height); + Rect bounds = new Rect(itemHorizontalOffset, top, state.ColumnWidth, item.Height); item.Arrange(bounds); - - columnHeights[columnIndex] += item.Height; - itemsPerColumn[columnIndex]++; + top += item.Height; } else { From 2b66a46ada1951ac35e546b4e5c5deafb7dcf3cd Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:04:45 -0700 Subject: [PATCH 023/105] Make sure we remove the state --- .../StaggeredLayout/StaggeredLayout.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 0982e66998e..a931a598de7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -111,6 +111,13 @@ protected override void InitializeForContextCore(VirtualizingLayoutContext conte base.InitializeForContextCore(context); } + /// + protected override void UninitializeForContextCore(VirtualizingLayoutContext context) + { + context.LayoutState = null; + base.UninitializeForContextCore(context); + } + /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { From 7767b7c23dbdba4d92037edf3e93e02386f5d1b8 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:05:28 -0700 Subject: [PATCH 024/105] Rename method to clear all the things --- .../StaggeredLayout/StaggeredLayout.cs | 2 +- .../StaggeredLayout/StaggeredLayoutState.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index a931a598de7..21b2a0cefe0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -139,7 +139,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double columnWidth = Math.Min(DesiredColumnWidth, availableWidth); if (columnWidth != state.ColumnWidth) { - state.ClearColumns(); + state.Clear(); } state.ColumnWidth = Math.Min(DesiredColumnWidth, availableWidth); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index c50b04e59b2..c7848aea509 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -56,7 +56,7 @@ internal StaggeredColumnLayout GetColumnLayout(int columnIndex) return columnLayout; } - internal void ClearColumns() + internal void Clear() { _columnLayout.Clear(); _items.Clear(); From 3931d9d118ceaed95c989cae37c81b189b166cdc Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:08:11 -0700 Subject: [PATCH 025/105] When the width of the panel changes we could get more columns to use. In that case clear out the cache of columns --- .../StaggeredLayout/StaggeredLayout.cs | 6 ++++++ .../StaggeredLayout/StaggeredLayoutState.cs | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 21b2a0cefe0..1cca2fddc75 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -139,6 +139,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double columnWidth = Math.Min(DesiredColumnWidth, availableWidth); if (columnWidth != state.ColumnWidth) { + // The items will need to be remeasured state.Clear(); } @@ -155,6 +156,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { availableWidth = totalWidth; } + if (numColumns != state.NumberOfColumns) + { + // The items will not need to be remeasured, but they will need to go into new columns + state.ClearColumns(); + } var columnHeights = new double[numColumns]; var itemsPerColumn = new double[numColumns]; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index c7848aea509..a6a13a21bc1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -21,6 +21,8 @@ public StaggeredLayoutState(VirtualizingLayoutContext context) public double ColumnWidth { get; internal set; } + public int NumberOfColumns { get { return _columnLayout.Count; } } + internal void AddItemToColumn(StaggeredItem item, int columnIndex) { if (_columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout) == false) @@ -61,5 +63,10 @@ internal void Clear() _columnLayout.Clear(); _items.Clear(); } + + internal void ClearColumns() + { + _columnLayout.Clear(); + } } } From a52d4ab2a5f2af6e3749a8ed8a8e076840d3ee0e Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:21:57 -0700 Subject: [PATCH 026/105] We ALWAYS want to measure an item that will be in the bounds --- .../StaggeredLayout/StaggeredLayout.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 1cca2fddc75..fd3ce36ea2d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -156,6 +156,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { availableWidth = totalWidth; } + if (numColumns != state.NumberOfColumns) { // The items will not need to be remeasured, but they will need to go into new columns @@ -191,6 +192,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.RecycleElement(); deadColumns.Add(columnIndex); } + else + { + // We ALWAYS want to measure an item that will be in the bounds + context.GetOrCreateElementAt(i).Measure(new Size(state.ColumnWidth, availableHeight)); + } if (deadColumns.Count == numColumns) { From a64783aa4a22c952df44c61cab07357b2f1be52c Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:29:25 -0700 Subject: [PATCH 027/105] Calculate the height based on the number of average number of items we have or, if we have all of the items use the total height --- .../StaggeredLayout/StaggeredColumnLayout.cs | 13 +++++++ .../StaggeredLayout/StaggeredLayout.cs | 2 +- .../StaggeredLayout/StaggeredLayoutState.cs | 34 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs index 9c63ab97b9e..b62ba1cb80c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs @@ -8,5 +8,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { internal class StaggeredColumnLayout : List { + public double Height { get; private set; } + + public new void Add(StaggeredItem item) + { + Height = item.Top + item.Height; + base.Add(item); + } + + public new void Clear() + { + Height = 0; + base.Clear(); + } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index fd3ce36ea2d..365cf584661 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -204,7 +204,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size } } - double desiredHeight = columnHeights.Max(); + double desiredHeight = state.GetHeight(); return new Size(availableWidth, desiredHeight); } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index a6a13a21bc1..b0cb6365a00 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls @@ -13,6 +14,7 @@ internal class StaggeredLayoutState private List _items = new List(); private VirtualizingLayoutContext _context; private Dictionary _columnLayout = new Dictionary(); + private double _lastAverageHeight; public StaggeredLayoutState(VirtualizingLayoutContext context) { @@ -68,5 +70,37 @@ internal void ClearColumns() { _columnLayout.Clear(); } + + internal double GetHeight() + { + double desiredHeight = Enumerable.Max(_columnLayout.Values, c => c.Height); + + var itemCount = Enumerable.Sum(_columnLayout.Values, c => c.Count); + if (itemCount == _context.ItemCount) + { + return desiredHeight; + } + + double averageHeight = 0; + foreach (var kvp in _columnLayout) + { + averageHeight += kvp.Value.Height / kvp.Value.Count; + } + + averageHeight /= _columnLayout.Count; + double estimatedHeight = (averageHeight * _context.ItemCount) / _columnLayout.Count; + if (estimatedHeight > desiredHeight) + { + desiredHeight = estimatedHeight; + } + + if (Math.Abs(desiredHeight - _lastAverageHeight) < 5) + { + return _lastAverageHeight; + } + + _lastAverageHeight = desiredHeight; + return desiredHeight; + } } } From 9899415bddb6277a584bcd24fd7a1e67e5f332c9 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 10 Feb 2020 17:30:15 -0700 Subject: [PATCH 028/105] Remove manual recycling logic --- .../StaggeredLayout/StaggeredLayout.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 365cf584661..2fd183ce8ff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -184,12 +184,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (bottom < context.RealizationRect.Top) { // The bottom of the element is above the realization area - item.RecycleElement(); + //item.RecycleElement(); } else if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area - item.RecycleElement(); + //item.RecycleElement(); deadColumns.Add(columnIndex); } else From 3a8ab8825eddab396aa5eb908008a35a57450ece Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 13:39:20 -0700 Subject: [PATCH 029/105] Remove padding property to be in line with the WinUI layouts --- .../StaggeredLayout/StaggeredLayout.cs | 40 ++++--------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 2fd183ce8ff..26d4ef49392 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -8,6 +8,7 @@ using Microsoft.UI.Xaml.Controls; using Windows.Foundation; using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -45,29 +46,6 @@ public double DesiredColumnWidth typeof(StaggeredLayout), new PropertyMetadata(250d, OnDesiredColumnWidthChanged)); - /// - /// Gets or sets the distance between the border and its child object. - /// - /// - /// The dimensions of the space between the border and its child as a Thickness value. - /// Thickness is a structure that stores dimension values using pixel measures. - /// - public Thickness Padding - { - get { return (Thickness)GetValue(PaddingProperty); } - set { SetValue(PaddingProperty, value); } - } - - /// - /// Identifies the Padding dependency property. - /// - /// The identifier for the dependency property. - public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register( - nameof(Padding), - typeof(Thickness), - typeof(StaggeredLayout), - new PropertyMetadata(default(Thickness), OnPaddingChanged)); - /// /// Gets or sets the spacing between columns of items. /// @@ -84,7 +62,7 @@ public double ColumnSpacing nameof(ColumnSpacing), typeof(double), typeof(StaggeredLayout), - new PropertyMetadata(0d, OnPaddingChanged)); + new PropertyMetadata(0d, OnSpacingChanged)); /// /// Gets or sets the spacing between rows of items. @@ -102,7 +80,7 @@ public double RowSpacing nameof(RowSpacing), typeof(double), typeof(StaggeredLayout), - new PropertyMetadata(0d, OnPaddingChanged)); + new PropertyMetadata(0d, OnSpacingChanged)); /// protected override void InitializeForContextCore(VirtualizingLayoutContext context) @@ -133,8 +111,8 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var state = (StaggeredLayoutState)context.LayoutState; - double availableWidth = availableSize.Width - Padding.Left - Padding.Right; - double availableHeight = availableSize.Height - Padding.Top - Padding.Bottom; + double availableWidth = availableSize.Width; + double availableHeight = availableSize.Height; double columnWidth = Math.Min(DesiredColumnWidth, availableWidth); if (columnWidth != state.ColumnWidth) @@ -219,8 +197,6 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var state = (StaggeredLayoutState)context.LayoutState; - double horizontalOffset = Padding.Left; - double verticalOffset = Padding.Top; int numColumns = Math.Max(1, (int)Math.Floor(finalSize.Width / state.ColumnWidth)); // adjust for horizontal spacing on all columns expect the first @@ -235,7 +211,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) { - double top = verticalOffset; + double top = 0; StaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); for (int i = 0; i < layout.Count; i++) { @@ -255,7 +231,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size if (top <= context.RealizationRect.Bottom) { - double itemHorizontalOffset = horizontalOffset + (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); + double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); Rect bounds = new Rect(itemHorizontalOffset, top, state.ColumnWidth, item.Height); item.Arrange(bounds); @@ -277,7 +253,7 @@ private static void OnDesiredColumnWidthChanged(DependencyObject d, DependencyPr panel.InvalidateMeasure(); } - private static void OnPaddingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var panel = (StaggeredLayout)d; panel.InvalidateMeasure(); From fe0b6c39aa251dccbca11c9177e62a55e2ce4e81 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 13:43:43 -0700 Subject: [PATCH 030/105] Little code Clean up --- .../StaggeredLayout/StaggeredLayout.cs | 6 +++--- .../StaggeredLayout/StaggeredLayoutState.cs | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 26d4ef49392..8c5080cdb7b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -162,12 +162,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (bottom < context.RealizationRect.Top) { // The bottom of the element is above the realization area - //item.RecycleElement(); + // item.RecycleElement(); } else if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area - //item.RecycleElement(); + // item.RecycleElement(); deadColumns.Add(columnIndex); } else @@ -224,8 +224,8 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size double bottom = top + item.Height; if (bottom < context.RealizationRect.Top) { - top += item.Height; // element is above the realization bounds + top += item.Height; continue; } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index b0cb6365a00..9a45b216732 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -32,6 +32,7 @@ internal void AddItemToColumn(StaggeredItem item, int columnIndex) columnLayout = new StaggeredColumnLayout(); _columnLayout[columnIndex] = columnLayout; } + if (columnLayout.Contains(item) == false) { columnLayout.Add(item); @@ -40,7 +41,10 @@ internal void AddItemToColumn(StaggeredItem item, int columnIndex) internal StaggeredItem GetItemAt(int index) { - if (index < 0) throw new IndexOutOfRangeException(); + if (index < 0) + { + throw new IndexOutOfRangeException(); + } if (index <= (_items.Count - 1)) { From 235d5a601e0aa4661d37ccf572b737cdfca355e5 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 13:47:36 -0700 Subject: [PATCH 031/105] Add some comments --- .../StaggeredLayout/StaggeredLayoutState.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 9a45b216732..e0ad2556f6a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -64,17 +64,31 @@ internal StaggeredColumnLayout GetColumnLayout(int columnIndex) return columnLayout; } + /// + /// Clear everything that has been calculated. + /// internal void Clear() { _columnLayout.Clear(); _items.Clear(); } + /// + /// Clear the layout columns so they will be recalculated. + /// internal void ClearColumns() { _columnLayout.Clear(); } + /// + /// Gets the estimated height of the layout. + /// + /// The estimated height of the layout. + /// + /// If all of the items have been calculated then the actual height will be returned. + /// If all of the items have not been calculated then an estimated height will be calculated based on the average height of the items. + /// internal double GetHeight() { double desiredHeight = Enumerable.Max(_columnLayout.Values, c => c.Height); From 7722f4709accb9442244334520205d9b98c2cc62 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 13:48:33 -0700 Subject: [PATCH 032/105] Remove method not used anymore --- .../StaggeredLayout/StaggeredLayout.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 8c5080cdb7b..27b3f0cfda7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -259,11 +259,6 @@ private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChang panel.InvalidateMeasure(); } - private void OnHorizontalAlignmentChanged(DependencyObject sender, DependencyProperty dp) - { - InvalidateMeasure(); - } - private int GetColumnIndex(double[] columnHeights) { int columnIndex = 0; From 97fa1567eb843428e09b290ec19eaa91228fde09 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 13:52:39 -0700 Subject: [PATCH 033/105] We've already calculated the top. Use that value for placement --- .../StaggeredLayout/StaggeredLayout.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 27b3f0cfda7..2bfd8025bb1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -209,33 +209,27 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); } + // Cycle through each column and arrange the items that are within the realization bounds for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) { - double top = 0; StaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); for (int i = 0; i < layout.Count; i++) { StaggeredItem item = layout[i]; - if (i > 0) - { - top += RowSpacing; - } - double bottom = top + item.Height; + double bottom = item.Top + item.Height; if (bottom < context.RealizationRect.Top) { // element is above the realization bounds - top += item.Height; continue; } - if (top <= context.RealizationRect.Bottom) + if (item.Top <= context.RealizationRect.Bottom) { double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); - Rect bounds = new Rect(itemHorizontalOffset, top, state.ColumnWidth, item.Height); + Rect bounds = new Rect(itemHorizontalOffset, item.Top, state.ColumnWidth, item.Height); item.Arrange(bounds); - top += item.Height; } else { From 042d970112ba2de5c644be452aa6a14c0ba97f72 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 14:09:04 -0700 Subject: [PATCH 034/105] Remove items from the layout state when an item is removed from the layout itself --- .../StaggeredLayout/StaggeredLayout.cs | 14 ++++++++++++++ .../StaggeredLayout/StaggeredLayoutState.cs | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 2bfd8025bb1..d210035723b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using Microsoft.UI.Xaml.Controls; using Windows.Foundation; @@ -96,6 +97,19 @@ protected override void UninitializeForContextCore(VirtualizingLayoutContext con base.UninitializeForContextCore(context); } + /// + protected override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args) + { + var state = (StaggeredLayoutState)context.LayoutState; + + if (args.Action == NotifyCollectionChangedAction.Remove) + { + state.RemoveFromIndex(args.OldStartingIndex); + } + + base.OnItemsChangedCore(context, source, args); + } + /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index e0ad2556f6a..0729caf7db6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -120,5 +120,11 @@ internal double GetHeight() _lastAverageHeight = desiredHeight; return desiredHeight; } + + internal void RemoveFromIndex(int index) + { + int numToRemove = _items.Count - index; + _items.RemoveRange(index, numToRemove); + } } } From aefd2e0c1251137a052d71cac3cb94928bad7009 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 14:11:51 -0700 Subject: [PATCH 035/105] Clear the columns after the removal item --- .../StaggeredLayout/StaggeredItem.cs | 7 ++++--- .../StaggeredLayout/StaggeredLayoutState.cs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs index acb54e8a1b8..54e58872e6b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -12,19 +12,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls internal class StaggeredItem { private VirtualizingLayoutContext _context; - private int _index; private Size? _size; public StaggeredItem(VirtualizingLayoutContext context, int index) { _context = context; - this._index = index; + this.Index = index; } public double Top { get; internal set; } public double Height { get; internal set; } + public int Index { get; } + internal Size Measure(double columnWidth, double availableHeight) { if (_size == null) @@ -41,7 +42,7 @@ internal Size Measure(double columnWidth, double availableHeight) private UIElement GetElement() { - return _context.GetOrCreateElementAt(_index); + return _context.GetOrCreateElementAt(Index); } internal void Arrange(Rect bounds) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 0729caf7db6..3c40ecb8f9a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -125,6 +125,20 @@ internal void RemoveFromIndex(int index) { int numToRemove = _items.Count - index; _items.RemoveRange(index, numToRemove); + + foreach (var kvp in _columnLayout) + { + StaggeredColumnLayout layout = kvp.Value; + for (int i = 0; i < layout.Count; i++) + { + if (layout[i].Index >= index) + { + numToRemove = layout.Count - i; + layout.RemoveRange(i, numToRemove); + break; + } + } + } } } } From 0d2f5bb404d1f42b24a7803594763ae6c26520d4 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 14:26:02 -0700 Subject: [PATCH 036/105] We don't need to clear any of the cache if the an item was added/removed where we haven't cached --- .../StaggeredLayout/StaggeredLayoutState.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 3c40ecb8f9a..274098669c5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -123,6 +123,12 @@ internal double GetHeight() internal void RemoveFromIndex(int index) { + if (index > _items.Count) + { + // Item was added/removed but we haven't realized that far yet + return; + } + int numToRemove = _items.Count - index; _items.RemoveRange(index, numToRemove); From 64151247963be02d1f31c5e3d2c9716ab94e9c95 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 14:26:18 -0700 Subject: [PATCH 037/105] Clear cached items when an item is added --- .../StaggeredLayout/StaggeredLayout.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index d210035723b..9f4e3ecc39f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -106,6 +106,10 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob { state.RemoveFromIndex(args.OldStartingIndex); } + else if (args.Action == NotifyCollectionChangedAction.Add) + { + state.RemoveFromIndex(args.NewStartingIndex); + } base.OnItemsChangedCore(context, source, args); } From 33305e1375e734f378d7a9cb014a4a3ba11bfb41 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 14:28:05 -0700 Subject: [PATCH 038/105] Clear the entire cache when the collection has been reset --- .../StaggeredLayout/StaggeredLayout.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 9f4e3ecc39f..d910a755166 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -110,6 +110,10 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob { state.RemoveFromIndex(args.NewStartingIndex); } + else if (args.Action == NotifyCollectionChangedAction.Reset) + { + state.Clear(); + } base.OnItemsChangedCore(context, source, args); } From 519cfb1109283423c85de1f4d8a36878128d3cb9 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 14:33:36 -0700 Subject: [PATCH 039/105] No need to calculate the number of columns. We have it in our layout state --- .../StaggeredLayout/StaggeredLayout.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index d910a755166..fab0e47c779 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -219,20 +219,8 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var state = (StaggeredLayoutState)context.LayoutState; - int numColumns = Math.Max(1, (int)Math.Floor(finalSize.Width / state.ColumnWidth)); - - // adjust for horizontal spacing on all columns expect the first - double totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); - if (totalWidth > finalSize.Width) - { - numColumns--; - - // Need to recalculate the totalWidth for a correct horizontal offset - totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); - } - // Cycle through each column and arrange the items that are within the realization bounds - for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) + for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++) { StaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); for (int i = 0; i < layout.Count; i++) From b49dc3a866d616aac200b57aede047fb6f1ae73b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 15:53:52 -0700 Subject: [PATCH 040/105] First port of the WrapPanel to be a Layout --- .../WrapLayout/UvMeasure.cs | 27 ++ .../WrapLayout/WrapLayout.cs | 241 ++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs new file mode 100644 index 00000000000..a139056a85e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs @@ -0,0 +1,27 @@ +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal struct UvMeasure + { + internal static readonly UvMeasure Zero = default(UvMeasure); + + internal double U { get; set; } + + internal double V { get; set; } + + public UvMeasure(Orientation orientation, double width, double height) + { + if (orientation == Orientation.Horizontal) + { + U = width; + V = height; + } + else + { + U = height; + V = width; + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs new file mode 100644 index 00000000000..ebd8bb0eacf --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public class WrapLayout : VirtualizingLayout + { + /// + /// Gets or sets a uniform Horizontal distance (in pixels) between items when is set to Horizontal, + /// or between columns of items when is set to Vertical. + /// + public double HorizontalSpacing + { + get { return (double)GetValue(HorizontalSpacingProperty); } + set { SetValue(HorizontalSpacingProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty HorizontalSpacingProperty = + DependencyProperty.Register( + nameof(HorizontalSpacing), + typeof(double), + typeof(WrapLayout), + new PropertyMetadata(0d, LayoutPropertyChanged)); + + /// + /// Gets or sets a uniform Vertical distance (in pixels) between items when is set to Vertical, + /// or between rows of items when is set to Horizontal. + /// + public double VerticalSpacing + { + get { return (double)GetValue(VerticalSpacingProperty); } + set { SetValue(VerticalSpacingProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty VerticalSpacingProperty = + DependencyProperty.Register( + nameof(VerticalSpacing), + typeof(double), + typeof(WrapLayout), + new PropertyMetadata(0d, LayoutPropertyChanged)); + + /// + /// Gets or sets the orientation of the WrapLayout. + /// Horizontal means that child controls will be added horizontally until the width of the panel is reached, then a new row is added to add new child controls. + /// Vertical means that children will be added vertically until the height of the panel is reached, then a new column is added. + /// + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register( + nameof(Orientation), + typeof(Orientation), + typeof(WrapLayout), + new PropertyMetadata(Orientation.Horizontal, LayoutPropertyChanged)); + + /// + /// Gets or sets the distance between the border and its child object. + /// + /// + /// The dimensions of the space between the border and its child as a Thickness value. + /// Thickness is a structure that stores dimension values using pixel measures. + /// + public Thickness Padding + { + get { return (Thickness)GetValue(PaddingProperty); } + set { SetValue(PaddingProperty, value); } + } + + /// + /// Identifies the Padding dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty PaddingProperty = + DependencyProperty.Register( + nameof(Padding), + typeof(Thickness), + typeof(WrapLayout), + new PropertyMetadata(default(Thickness), LayoutPropertyChanged)); + + private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is WrapLayout wp) + { + wp.InvalidateMeasure(); + wp.InvalidateArrange(); + } + } + + /// + protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) + { + availableSize.Width = availableSize.Width - Padding.Left - Padding.Right; + availableSize.Height = availableSize.Height - Padding.Top - Padding.Bottom; + var totalMeasure = UvMeasure.Zero; + var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); + var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); + var lineMeasure = UvMeasure.Zero; + + for (int i = 0; i < context.ItemCount; i++) + { + UIElement child = context.GetOrCreateElementAt(i); + child.Measure(availableSize); + var currentMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + if (currentMeasure.U == 0) + { + continue; // ignore collapsed items + } + + // if this is the first item, do not add spacing. Spacing is added to the "left" + double uChange = lineMeasure.U == 0 + ? currentMeasure.U + : currentMeasure.U + spacingMeasure.U; + if (parentMeasure.U >= uChange + lineMeasure.U) + { + lineMeasure.U += uChange; + lineMeasure.V = Math.Max(lineMeasure.V, currentMeasure.V); + } + else + { + // new line should be added + // to get the max U to provide it correctly to ui width ex: ---| or -----| + totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); + totalMeasure.V += lineMeasure.V + spacingMeasure.V; + + // if the next new row still can handle more controls + if (parentMeasure.U > currentMeasure.U) + { + // set lineMeasure initial values to the currentMeasure to be calculated later on the new loop + lineMeasure = currentMeasure; + } + + // the control will take one row alone + else + { + // validate the new control measures + totalMeasure.U = Math.Max(currentMeasure.U, totalMeasure.U); + totalMeasure.V += currentMeasure.V; + + // add new empty line + lineMeasure = UvMeasure.Zero; + } + } + } + + // update value with the last line + // if the the last loop is(parentMeasure.U > currentMeasure.U + lineMeasure.U) the total isn't calculated then calculate it + // if the last loop is (parentMeasure.U > currentMeasure.U) the currentMeasure isn't added to the total so add it here + // for the last condition it is zeros so adding it will make no difference + // this way is faster than an if condition in every loop for checking the last item + totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); + totalMeasure.V += lineMeasure.V; + + totalMeasure.U = Math.Ceiling(totalMeasure.U); + + return Orientation == Orientation.Horizontal ? new Size(totalMeasure.U, totalMeasure.V) : new Size(totalMeasure.V, totalMeasure.U); + } + + /// + protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) + { + if (context.ItemCount > 0) + { + var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); + var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); + var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); + var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); + var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); + + double currentV = 0; + void arrange(UIElement child, bool isLast = false) + { + var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + if (desiredMeasure.U == 0) + { + return; // if an item is collapsed, avoid adding the spacing + } + + if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) + { + // next row! + position.U = paddingStart.U; + position.V += currentV + spacingMeasure.V; + currentV = 0; + } + + // Stretch the last item to fill the available space + if (isLast) + { + desiredMeasure.U = parentMeasure.U - position.U; + } + + // place the item + if (Orientation == Orientation.Horizontal) + { + child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V)); + } + else + { + child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U)); + } + + // adjust the location for the next items + position.U += desiredMeasure.U + spacingMeasure.U; + currentV = Math.Max(desiredMeasure.V, currentV); + } + + for (var i = 0; i < context.ItemCount; i++) + { + arrange(context.GetOrCreateElementAt(i)); + } + } + + return finalSize; + } + } + +} From 38e81fe89535be2c183e0b4723311f2720884048 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 11 Feb 2020 16:09:26 -0700 Subject: [PATCH 041/105] Stop measuring items when we are outside the bounds --- .../WrapLayout/UvBounds.cs | 39 +++++++++++++++++++ .../WrapLayout/WrapLayout.cs | 6 +++ 2 files changed, 45 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs new file mode 100644 index 00000000000..b21492c1eb4 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.Foundation; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal struct UvBounds + { + + public UvBounds(Orientation orientation, Rect rect) + { + if (orientation == Orientation.Horizontal) + { + UMin = rect.Left; + UMax = rect.Right; + VMin = rect.Top; + VMax = rect.Bottom; + } + else + { + UMin = rect.Top; + UMax = rect.Bottom; + VMin = rect.Left; + VMax = rect.Right; + } + } + + public double UMin { get; } + + public double UMax { get; } + + public double VMin { get; } + + public double VMax { get; } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index ebd8bb0eacf..0797425cc41 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -118,6 +118,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var totalMeasure = UvMeasure.Zero; var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); + var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var lineMeasure = UvMeasure.Zero; for (int i = 0; i < context.ItemCount; i++) @@ -163,6 +164,11 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // add new empty line lineMeasure = UvMeasure.Zero; } + + if (totalMeasure.V > realizationBounds.VMax) + { + break; + } } } From 975dc7b326fb12ae4bb1f4be964bd3ba9ebc2c5e Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:07:07 -0700 Subject: [PATCH 042/105] Only arrange items within the bounds --- .../WrapLayout/WrapLayout.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 0797425cc41..9143e7e79fd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -195,6 +195,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); + var realizationBounds = new UvBounds(Orientation, context.RealizationRect); double currentV = 0; void arrange(UIElement child, bool isLast = false) @@ -219,14 +220,17 @@ void arrange(UIElement child, bool isLast = false) desiredMeasure.U = parentMeasure.U - position.U; } - // place the item - if (Orientation == Orientation.Horizontal) + if ((position.V >= realizationBounds.VMin) && (position.V <= realizationBounds.VMax)) { - child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V)); - } - else - { - child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U)); + // place the item + if (Orientation == Orientation.Horizontal) + { + child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V)); + } + else + { + child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U)); + } } // adjust the location for the next items From 97a2813bf732745a227aba79dda38c75090c2653 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:08:34 -0700 Subject: [PATCH 043/105] The bottom of the items needs to be within the top bounds. --- .../WrapLayout/WrapLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 9143e7e79fd..4bd25cb3ec3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -220,7 +220,7 @@ void arrange(UIElement child, bool isLast = false) desiredMeasure.U = parentMeasure.U - position.U; } - if ((position.V >= realizationBounds.VMin) && (position.V <= realizationBounds.VMax)) + if (((position.V + desiredMeasure.V) >= realizationBounds.VMin) && (position.V <= realizationBounds.VMax)) { // place the item if (Orientation == Orientation.Horizontal) From 72a39b5c26e3bb3e50a0a16818ee6e12ccf8d5c3 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:11:17 -0700 Subject: [PATCH 044/105] Keep track of the element measure and only get the element when we need to --- .../WrapLayout/UvBounds.cs | 1 - .../WrapLayout/WrapItem.cs | 18 +++++++++ .../WrapLayout/WrapLayout.cs | 36 ++++++++++++++---- .../WrapLayout/WrapLayoutState.cs | 38 +++++++++++++++++++ 4 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs index b21492c1eb4..616275c4d99 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvBounds.cs @@ -9,7 +9,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { internal struct UvBounds { - public UvBounds(Orientation orientation, Rect rect) { if (orientation == Orientation.Horizontal) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs new file mode 100644 index 00000000000..fca61c25847 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal class WrapItem + { + public WrapItem(int index) + { + this.Index = index; + } + + public int Index { get; } + + public UvMeasure? Measure { get; internal set; } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 4bd25cb3ec3..5a5f9f02767 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -110,6 +110,18 @@ private static void LayoutPropertyChanged(DependencyObject d, DependencyProperty } } + protected override void InitializeForContextCore(VirtualizingLayoutContext context) + { + context.LayoutState = new WrapLayoutState(); + base.InitializeForContextCore(context); + } + + protected override void UninitializeForContextCore(VirtualizingLayoutContext context) + { + context.LayoutState = null; + base.UninitializeForContextCore(context); + } + /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { @@ -121,11 +133,18 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var lineMeasure = UvMeasure.Zero; + var state = (WrapLayoutState)context.LayoutState; for (int i = 0; i < context.ItemCount; i++) { - UIElement child = context.GetOrCreateElementAt(i); - child.Measure(availableSize); - var currentMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + WrapItem item = state.GetItemAt(i); + if (item.Measure == null) + { + UIElement child = context.GetOrCreateElementAt(i); + child.Measure(availableSize); + item.Measure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + } + + UvMeasure currentMeasure = item.Measure.Value; if (currentMeasure.U == 0) { continue; // ignore collapsed items @@ -198,9 +217,10 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var realizationBounds = new UvBounds(Orientation, context.RealizationRect); double currentV = 0; - void arrange(UIElement child, bool isLast = false) + var state = (WrapLayoutState)context.LayoutState; + void arrange(WrapItem item, bool isLast = false) { - var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + var desiredMeasure = item.Measure.Value; if (desiredMeasure.U == 0) { return; // if an item is collapsed, avoid adding the spacing @@ -223,6 +243,7 @@ void arrange(UIElement child, bool isLast = false) if (((position.V + desiredMeasure.V) >= realizationBounds.VMin) && (position.V <= realizationBounds.VMax)) { // place the item + UIElement child = context.GetOrCreateElementAt(item.Index); if (Orientation == Orientation.Horizontal) { child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V)); @@ -240,12 +261,11 @@ void arrange(UIElement child, bool isLast = false) for (var i = 0; i < context.ItemCount; i++) { - arrange(context.GetOrCreateElementAt(i)); + arrange(state.GetItemAt(i)); } } return finalSize; } } - -} +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs new file mode 100644 index 00000000000..60d02dd231f --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal class WrapLayoutState + { + private List _items = new List(); + + public WrapLayoutState() + { + } + + internal WrapItem GetItemAt(int index) + { + if (index < 0) + { + throw new IndexOutOfRangeException(); + } + + if (index <= (_items.Count - 1)) + { + return _items[index]; + } + else + { + WrapItem item = new WrapItem(index); + _items.Add(item); + return item; + } + } + + } +} \ No newline at end of file From fb3ae6516a41379aecf4b96e44abb6c53263358b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:14:31 -0700 Subject: [PATCH 045/105] break out of the arrange when we have passed the realization bounds --- .../WrapLayout/WrapLayout.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 5a5f9f02767..27ee04acabe 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -218,12 +218,12 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size double currentV = 0; var state = (WrapLayoutState)context.LayoutState; - void arrange(WrapItem item, bool isLast = false) + bool arrange(WrapItem item, bool isLast = false) { var desiredMeasure = item.Measure.Value; if (desiredMeasure.U == 0) { - return; // if an item is collapsed, avoid adding the spacing + return true; // if an item is collapsed, avoid adding the spacing } if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) @@ -253,15 +253,25 @@ void arrange(WrapItem item, bool isLast = false) child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U)); } } + else if (position.V > realizationBounds.VMax) + { + return false; + } // adjust the location for the next items position.U += desiredMeasure.U + spacingMeasure.U; currentV = Math.Max(desiredMeasure.V, currentV); + + return true; } for (var i = 0; i < context.ItemCount; i++) { - arrange(state.GetItemAt(i)); + bool continueArranging = arrange(state.GetItemAt(i)); + if (continueArranging == false) + { + break; + } } } From 8d642998fb1a9effa3e69188c0fa96862801a74f Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:23:16 -0700 Subject: [PATCH 046/105] Always measure items that are within the bounds --- .../WrapLayout/WrapLayout.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 27ee04acabe..a1595208008 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -136,12 +136,14 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var state = (WrapLayoutState)context.LayoutState; for (int i = 0; i < context.ItemCount; i++) { + bool measured = false; WrapItem item = state.GetItemAt(i); if (item.Measure == null) { UIElement child = context.GetOrCreateElementAt(i); child.Measure(availableSize); item.Measure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + measured = true; } UvMeasure currentMeasure = item.Measure.Value; @@ -183,11 +185,24 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // add new empty line lineMeasure = UvMeasure.Zero; } + } - if (totalMeasure.V > realizationBounds.VMax) - { - break; - } + if (totalMeasure.V > realizationBounds.VMax) + { + // Item is "below" the bounds. + break; + } + + double vEnd = totalMeasure.V + currentMeasure.V; + if (vEnd < realizationBounds.VMin) + { + // Item is "above" the bounds + } + else if (measured == false) + { + // Always measure elements that are within the bounds + UIElement child = context.GetOrCreateElementAt(i); + child.Measure(availableSize); } } From 68dc5e5400d6b3ad11ad14f33083717a50899fd1 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:27:47 -0700 Subject: [PATCH 047/105] Add the tag --- .../WrapLayout/WrapLayout.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index a1595208008..3bd783b5fc4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -110,12 +110,14 @@ private static void LayoutPropertyChanged(DependencyObject d, DependencyProperty } } + /// protected override void InitializeForContextCore(VirtualizingLayoutContext context) { context.LayoutState = new WrapLayoutState(); base.InitializeForContextCore(context); } + /// protected override void UninitializeForContextCore(VirtualizingLayoutContext context) { context.LayoutState = null; From 07cee0b221aa07124b57126cadb7ccd2100c6e09 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:35:30 -0700 Subject: [PATCH 048/105] Clear items when the collection changes --- .../WrapLayout/WrapLayout.cs | 22 +++++++++++++++++++ .../WrapLayout/WrapLayoutState.cs | 16 ++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 3bd783b5fc4..c5a30d4e9b8 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -124,6 +125,27 @@ protected override void UninitializeForContextCore(VirtualizingLayoutContext con base.UninitializeForContextCore(context); } + /// + protected override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args) + { + var state = (WrapLayoutState)context.LayoutState; + + if (args.Action == NotifyCollectionChangedAction.Remove) + { + state.RemoveFromIndex(args.OldStartingIndex); + } + else if (args.Action == NotifyCollectionChangedAction.Add) + { + state.RemoveFromIndex(args.NewStartingIndex); + } + else if (args.Action == NotifyCollectionChangedAction.Reset) + { + state.Clear(); + } + + base.OnItemsChangedCore(context, source, args); + } + /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 60d02dd231f..c8d55e937eb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -34,5 +34,21 @@ internal WrapItem GetItemAt(int index) } } + internal void Clear() + { + _items.Clear(); + } + + internal void RemoveFromIndex(int index) + { + if (index > _items.Count) + { + // Item was added/removed but we haven't realized that far yet + return; + } + + int numToRemove = _items.Count - index; + _items.RemoveRange(index, numToRemove); + } } } \ No newline at end of file From 3e6b7a36b417467e79cd13a14343d9035bc025ca Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 09:58:56 -0700 Subject: [PATCH 049/105] When the Orientation changes, clear the cache --- .../WrapLayout/WrapLayout.cs | 6 ++++++ .../WrapLayout/WrapLayoutState.cs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index c5a30d4e9b8..4fd29f914f4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -158,6 +158,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var lineMeasure = UvMeasure.Zero; var state = (WrapLayoutState)context.LayoutState; + if (state.Orientation != Orientation) + { + state.Clear(); + state.Orientation = Orientation; + } + for (int i = 0; i < context.ItemCount; i++) { bool measured = false; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index c8d55e937eb..d69884bc230 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -11,10 +12,13 @@ internal class WrapLayoutState { private List _items = new List(); + public WrapLayoutState() { } + public Orientation Orientation { get; internal set; } + internal WrapItem GetItemAt(int index) { if (index < 0) From 7e5a391640d643e56de2e0e9aa4c72d7fe22b961 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 10:29:35 -0700 Subject: [PATCH 050/105] No need to clear the entire collection. We can just switch the values --- .../WrapLayout/WrapLayout.cs | 3 +-- .../WrapLayout/WrapLayoutState.cs | 20 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 4fd29f914f4..6611453113d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -160,8 +160,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var state = (WrapLayoutState)context.LayoutState; if (state.Orientation != Orientation) { - state.Clear(); - state.Orientation = Orientation; + state.SetOrientation(Orientation); } for (int i = 0; i < context.ItemCount; i++) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index d69884bc230..e4f26b04b36 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls @@ -12,13 +13,12 @@ internal class WrapLayoutState { private List _items = new List(); - public WrapLayoutState() { } - public Orientation Orientation { get; internal set; } - + public Orientation Orientation { get; private set; } + internal WrapItem GetItemAt(int index) { if (index < 0) @@ -54,5 +54,19 @@ internal void RemoveFromIndex(int index) int numToRemove = _items.Count - index; _items.RemoveRange(index, numToRemove); } + + internal void SetOrientation(Orientation orientation) + { + foreach (var item in _items.Where(i => i.Measure.HasValue)) + { + UvMeasure measure = item.Measure.Value; + double v = measure.V; + measure.V = measure.U; + measure.U = v; + item.Measure = measure; + } + + Orientation = orientation; + } } } \ No newline at end of file From 5e226e154cf1b83865e499627e9bb8562ef7d4c4 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 10:36:46 -0700 Subject: [PATCH 051/105] Only check if we're past the bounds when we move to the next row --- .../WrapLayout/WrapLayout.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 6611453113d..e2321b167e4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -214,12 +214,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // add new empty line lineMeasure = UvMeasure.Zero; } - } - if (totalMeasure.V > realizationBounds.VMax) - { - // Item is "below" the bounds. - break; + if (totalMeasure.V > realizationBounds.VMax) + { + // Item is "below" the bounds. + break; + } } double vEnd = totalMeasure.V + currentMeasure.V; From f4a52ddbfdc5bc9d383966d241475605af4a062b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 12 Feb 2020 10:46:11 -0700 Subject: [PATCH 052/105] Remove Padding to be in line with the WinUI Layouts --- .../WrapLayout/WrapLayout.cs | 36 +++---------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index e2321b167e4..eee97a19dc0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -78,30 +78,6 @@ public Orientation Orientation typeof(WrapLayout), new PropertyMetadata(Orientation.Horizontal, LayoutPropertyChanged)); - /// - /// Gets or sets the distance between the border and its child object. - /// - /// - /// The dimensions of the space between the border and its child as a Thickness value. - /// Thickness is a structure that stores dimension values using pixel measures. - /// - public Thickness Padding - { - get { return (Thickness)GetValue(PaddingProperty); } - set { SetValue(PaddingProperty, value); } - } - - /// - /// Identifies the Padding dependency property. - /// - /// The identifier for the dependency property. - public static readonly DependencyProperty PaddingProperty = - DependencyProperty.Register( - nameof(Padding), - typeof(Thickness), - typeof(WrapLayout), - new PropertyMetadata(default(Thickness), LayoutPropertyChanged)); - private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is WrapLayout wp) @@ -149,8 +125,8 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { - availableSize.Width = availableSize.Width - Padding.Left - Padding.Right; - availableSize.Height = availableSize.Height - Padding.Top - Padding.Bottom; + availableSize.Width = availableSize.Width; + availableSize.Height = availableSize.Height; var totalMeasure = UvMeasure.Zero; var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); @@ -255,9 +231,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size { var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); - var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); - var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); - var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); + var position = UvMeasure.Zero; var realizationBounds = new UvBounds(Orientation, context.RealizationRect); double currentV = 0; @@ -270,10 +244,10 @@ bool arrange(WrapItem item, bool isLast = false) return true; // if an item is collapsed, avoid adding the spacing } - if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) + if ((desiredMeasure.U + position.U) > parentMeasure.U) { // next row! - position.U = paddingStart.U; + position.U = 0; position.V += currentV + spacingMeasure.V; currentV = 0; } From 6b4f54e640abf1c6f5e54812c69f7fe0e13b6769 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 13 Feb 2020 10:12:28 -0700 Subject: [PATCH 053/105] Add debugger info --- Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs index a139056a85e..37a1cdc1dcb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs @@ -2,6 +2,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { + [System.Diagnostics.DebuggerDisplay("U = {U} V = {V}")] internal struct UvMeasure { internal static readonly UvMeasure Zero = default(UvMeasure); From 89e08cd4d67220de9da04d5b9f7c8e97b74d7d2b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 12:06:09 -0700 Subject: [PATCH 054/105] Store the position of items instead of calculating each time --- .../WrapLayout/WrapItem.cs | 2 + .../WrapLayout/WrapLayout.cs | 86 +++++++------------ 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs index fca61c25847..40f22a62dfc 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs @@ -14,5 +14,7 @@ public WrapItem(int index) public int Index { get; } public UvMeasure? Measure { get; internal set; } + + public UvMeasure? Position { get; internal set; } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index eee97a19dc0..3be511d0591 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -125,13 +125,12 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob /// protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { - availableSize.Width = availableSize.Width; - availableSize.Height = availableSize.Height; var totalMeasure = UvMeasure.Zero; var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var lineMeasure = UvMeasure.Zero; + var position = UvMeasure.Zero; var state = (WrapLayoutState)context.LayoutState; if (state.Orientation != Orientation) @@ -139,6 +138,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size state.SetOrientation(Orientation); } + double currentV = 0; for (int i = 0; i < context.ItemCount; i++) { bool measured = false; @@ -157,58 +157,40 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size continue; // ignore collapsed items } - // if this is the first item, do not add spacing. Spacing is added to the "left" - double uChange = lineMeasure.U == 0 - ? currentMeasure.U - : currentMeasure.U + spacingMeasure.U; - if (parentMeasure.U >= uChange + lineMeasure.U) + if (item.Position == null) { - lineMeasure.U += uChange; - lineMeasure.V = Math.Max(lineMeasure.V, currentMeasure.V); - } - else - { - // new line should be added - // to get the max U to provide it correctly to ui width ex: ---| or -----| - totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); - totalMeasure.V += lineMeasure.V + spacingMeasure.V; - - // if the next new row still can handle more controls - if (parentMeasure.U > currentMeasure.U) - { - // set lineMeasure initial values to the currentMeasure to be calculated later on the new loop - lineMeasure = currentMeasure; - } - - // the control will take one row alone - else + if (parentMeasure.U < position.U + currentMeasure.U) { - // validate the new control measures - totalMeasure.U = Math.Max(currentMeasure.U, totalMeasure.U); - totalMeasure.V += currentMeasure.V; - - // add new empty line - lineMeasure = UvMeasure.Zero; + // New Row + position.U = 0; + position.V += currentV + spacingMeasure.V; + currentV = 0; } - if (totalMeasure.V > realizationBounds.VMax) - { - // Item is "below" the bounds. - break; - } + item.Position = position; } - double vEnd = totalMeasure.V + currentMeasure.V; + position = item.Position.Value; + + double vEnd = position.V + currentMeasure.V; if (vEnd < realizationBounds.VMin) { // Item is "above" the bounds } + else if (position.V > realizationBounds.VMax) + { + // Item is "below" the bounds. + break; + } else if (measured == false) { // Always measure elements that are within the bounds UIElement child = context.GetOrCreateElementAt(i); child.Measure(availableSize); } + + position.U += currentMeasure.U + spacingMeasure.U; + currentV = Math.Max(currentMeasure.V, currentV); } // update value with the last line @@ -216,8 +198,8 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // if the last loop is (parentMeasure.U > currentMeasure.U) the currentMeasure isn't added to the total so add it here // for the last condition it is zeros so adding it will make no difference // this way is faster than an if condition in every loop for checking the last item - totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); - totalMeasure.V += lineMeasure.V; + totalMeasure.U = parentMeasure.U; + totalMeasure.V = position.V; totalMeasure.U = Math.Ceiling(totalMeasure.U); @@ -231,26 +213,28 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size { var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); - var position = UvMeasure.Zero; var realizationBounds = new UvBounds(Orientation, context.RealizationRect); - double currentV = 0; var state = (WrapLayoutState)context.LayoutState; bool arrange(WrapItem item, bool isLast = false) { + if (item.Measure.HasValue == false) + { + return false; + } + + if (item.Position == null) + { + return false; + } + var desiredMeasure = item.Measure.Value; if (desiredMeasure.U == 0) { return true; // if an item is collapsed, avoid adding the spacing } - if ((desiredMeasure.U + position.U) > parentMeasure.U) - { - // next row! - position.U = 0; - position.V += currentV + spacingMeasure.V; - currentV = 0; - } + UvMeasure position = item.Position.Value; // Stretch the last item to fill the available space if (isLast) @@ -276,10 +260,6 @@ bool arrange(WrapItem item, bool isLast = false) return false; } - // adjust the location for the next items - position.U += desiredMeasure.U + spacingMeasure.U; - currentV = Math.Max(desiredMeasure.V, currentV); - return true; } From d7d417136e3d36eb0fdcb7bdab7d1afd6139c667 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 13:06:48 -0700 Subject: [PATCH 055/105] Reset the position of items whenever a layout property changes --- .../WrapLayout/WrapLayout.cs | 28 ++++++++++++++++++- .../WrapLayout/WrapLayoutState.cs | 8 ++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 3be511d0591..473736d5d18 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -82,6 +82,12 @@ private static void LayoutPropertyChanged(DependencyObject d, DependencyProperty { if (d is WrapLayout wp) { + WrapLayoutState state = GetLayoutState(wp); + if (state != null) + { + state.ClearPositions(); + } + wp.InvalidateMeasure(); wp.InvalidateArrange(); } @@ -90,7 +96,9 @@ private static void LayoutPropertyChanged(DependencyObject d, DependencyProperty /// protected override void InitializeForContextCore(VirtualizingLayoutContext context) { - context.LayoutState = new WrapLayoutState(); + var state = new WrapLayoutState(); + context.LayoutState = state; + SetLayoutState(this, state); base.InitializeForContextCore(context); } @@ -98,6 +106,7 @@ protected override void InitializeForContextCore(VirtualizingLayoutContext conte protected override void UninitializeForContextCore(VirtualizingLayoutContext context) { context.LayoutState = null; + SetLayoutState(this, null); base.UninitializeForContextCore(context); } @@ -275,5 +284,22 @@ bool arrange(WrapItem item, bool isLast = false) return finalSize; } + + private static WrapLayoutState GetLayoutState(WrapLayout obj) + { + return (WrapLayoutState)obj.GetValue(LayoutStateProperty); + } + + private static void SetLayoutState(WrapLayout obj, WrapLayoutState value) + { + obj.SetValue(LayoutStateProperty, value); + } + + private static readonly DependencyProperty LayoutStateProperty = + DependencyProperty.RegisterAttached( + "LayoutState", + typeof(WrapLayoutState), + typeof(WrapLayout), + new PropertyMetadata(null)); } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index e4f26b04b36..15ab637d7a8 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -68,5 +68,13 @@ internal void SetOrientation(Orientation orientation) Orientation = orientation; } + + internal void ClearPositions() + { + foreach (var item in _items) + { + item.Position = null; + } + } } } \ No newline at end of file From 2a1497774db36d38145ba5440045c72edd0c1614 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 13:09:31 -0700 Subject: [PATCH 056/105] Clear positions whenever the available U value changes --- .../WrapLayout/WrapLayout.cs | 6 ++++++ .../WrapLayout/WrapLayoutState.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 473736d5d18..47802a6aa1e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -147,6 +147,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size state.SetOrientation(Orientation); } + if (state.AvailableU != parentMeasure.U) + { + state.ClearPositions(); + state.AvailableU = parentMeasure.U; + } + double currentV = 0; for (int i = 0; i < context.ItemCount; i++) { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 15ab637d7a8..616c90c3bd6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -18,6 +18,7 @@ public WrapLayoutState() } public Orientation Orientation { get; private set; } + public double AvailableU { get; internal set; } internal WrapItem GetItemAt(int index) { From a940e55c77b6924bce15f9cd0a2771917ba0fb88 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 13:10:38 -0700 Subject: [PATCH 057/105] Clear the available U whenever orientation changes --- .../WrapLayout/WrapLayoutState.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 616c90c3bd6..7ff9b28a36a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -68,6 +68,7 @@ internal void SetOrientation(Orientation orientation) } Orientation = orientation; + AvailableU = 0; } internal void ClearPositions() From 1d8d591d7068174635b63c68c3a331d2c953ae90 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 13:13:37 -0700 Subject: [PATCH 058/105] Remove property not used --- .../WrapLayout/WrapLayout.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 47802a6aa1e..2d09437e483 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -138,7 +138,6 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var realizationBounds = new UvBounds(Orientation, context.RealizationRect); - var lineMeasure = UvMeasure.Zero; var position = UvMeasure.Zero; var state = (WrapLayoutState)context.LayoutState; From 711bfc42764ec942c68d0152879bef5388871db1 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 14:00:53 -0700 Subject: [PATCH 059/105] Make sure to null the position when orientation changes --- .../WrapLayout/WrapLayoutState.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 7ff9b28a36a..17cd89323ce 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -18,6 +18,7 @@ public WrapLayoutState() } public Orientation Orientation { get; private set; } + public double AvailableU { get; internal set; } internal WrapItem GetItemAt(int index) @@ -65,6 +66,7 @@ internal void SetOrientation(Orientation orientation) measure.V = measure.U; measure.U = v; item.Measure = measure; + item.Position = null; } Orientation = orientation; From 6ae10bee90539f241b3be71bf00dc4740c3a3af4 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 14:05:00 -0700 Subject: [PATCH 060/105] Remove the attached property and store the spacing in the state --- .../WrapLayout/UvMeasure.cs | 10 ++++++ .../WrapLayout/WrapLayout.cs | 31 ++++--------------- .../WrapLayout/WrapLayoutState.cs | 2 ++ 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs index 37a1cdc1dcb..c59f9c859ca 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs @@ -24,5 +24,15 @@ public UvMeasure(Orientation orientation, double width, double height) V = width; } } + + public override bool Equals(object obj) + { + if (obj is UvMeasure measure) + { + return (measure.U == U) && (measure.V == V); + } + + return false; + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 2d09437e483..33bf88d3bb1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -82,12 +82,6 @@ private static void LayoutPropertyChanged(DependencyObject d, DependencyProperty { if (d is WrapLayout wp) { - WrapLayoutState state = GetLayoutState(wp); - if (state != null) - { - state.ClearPositions(); - } - wp.InvalidateMeasure(); wp.InvalidateArrange(); } @@ -98,7 +92,6 @@ protected override void InitializeForContextCore(VirtualizingLayoutContext conte { var state = new WrapLayoutState(); context.LayoutState = state; - SetLayoutState(this, state); base.InitializeForContextCore(context); } @@ -106,7 +99,6 @@ protected override void InitializeForContextCore(VirtualizingLayoutContext conte protected override void UninitializeForContextCore(VirtualizingLayoutContext context) { context.LayoutState = null; - SetLayoutState(this, null); base.UninitializeForContextCore(context); } @@ -146,6 +138,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size state.SetOrientation(Orientation); } + if (spacingMeasure.Equals(state.Spacing) == false) + { + state.ClearPositions(); + state.Spacing = spacingMeasure; + } + if (state.AvailableU != parentMeasure.U) { state.ClearPositions(); @@ -289,22 +287,5 @@ bool arrange(WrapItem item, bool isLast = false) return finalSize; } - - private static WrapLayoutState GetLayoutState(WrapLayout obj) - { - return (WrapLayoutState)obj.GetValue(LayoutStateProperty); - } - - private static void SetLayoutState(WrapLayout obj, WrapLayoutState value) - { - obj.SetValue(LayoutStateProperty, value); - } - - private static readonly DependencyProperty LayoutStateProperty = - DependencyProperty.RegisterAttached( - "LayoutState", - typeof(WrapLayoutState), - typeof(WrapLayout), - new PropertyMetadata(null)); } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 17cd89323ce..1475dcf5ec7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -19,6 +19,8 @@ public WrapLayoutState() public Orientation Orientation { get; private set; } + public UvMeasure Spacing { get; internal set; } + public double AvailableU { get; internal set; } internal WrapItem GetItemAt(int index) From 100bff8c722817aaf3d61d5896601274c8724a5b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 15:58:17 -0700 Subject: [PATCH 061/105] Calculate the height of the items --- .../WrapLayout/WrapLayout.cs | 4 +- .../WrapLayout/WrapLayoutState.cs | 53 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 33bf88d3bb1..c901241c1d4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -90,7 +90,7 @@ private static void LayoutPropertyChanged(DependencyObject d, DependencyProperty /// protected override void InitializeForContextCore(VirtualizingLayoutContext context) { - var state = new WrapLayoutState(); + var state = new WrapLayoutState(context); context.LayoutState = state; base.InitializeForContextCore(context); } @@ -211,7 +211,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // for the last condition it is zeros so adding it will make no difference // this way is faster than an if condition in every loop for checking the last item totalMeasure.U = parentMeasure.U; - totalMeasure.V = position.V; + totalMeasure.V = state.GetHeight(); totalMeasure.U = Math.Ceiling(totalMeasure.U); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 1475dcf5ec7..fbbbcbe6ecc 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.UI.Xaml.Controls; using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls @@ -12,9 +13,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls internal class WrapLayoutState { private List _items = new List(); + private VirtualizingLayoutContext _context; - public WrapLayoutState() + public WrapLayoutState(VirtualizingLayoutContext context) { + this._context = context; } public Orientation Orientation { get; private set; } @@ -82,5 +85,53 @@ internal void ClearPositions() item.Position = null; } } + + internal double GetHeight() + { + if (_items.Count == 0) + { + return 0; + } + + bool calculateAvergae = true; + if ((_items.Count == _context.ItemCount) && _items[_items.Count - 1].Position.HasValue) + { + calculateAvergae = false; + } + + UvMeasure? lastPosition = null; + double maxV = 0; + + for (int i = _items.Count - 1; i >= 0; i--) + { + var item = _items[i]; + if (item.Position == null) + { + continue; + } + + if (lastPosition != null) + { + if (lastPosition.Value.V > item.Position.Value.V) + { + // This is a row above the last item. Exit and calculate the average + break; + } + } + + lastPosition = item.Position; + maxV = Math.Max(maxV, item.Measure.Value.V); + } + + double totalHeight = lastPosition.Value.V + maxV; + if (calculateAvergae) + { + return (totalHeight / _items.Count) * _context.ItemCount; + } + else + { + return totalHeight; + } + } } } \ No newline at end of file From 5808401695eeab86255940fcae6ce61df5b5b891 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 16:00:47 -0700 Subject: [PATCH 062/105] Don't average items that haven't had their position calculated yet --- .../WrapLayout/WrapLayoutState.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index fbbbcbe6ecc..1ce03ca6ab2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -102,11 +102,13 @@ internal double GetHeight() UvMeasure? lastPosition = null; double maxV = 0; + int itemCount = _items.Count; for (int i = _items.Count - 1; i >= 0; i--) { var item = _items[i]; if (item.Position == null) { + itemCount--; continue; } @@ -126,7 +128,7 @@ internal double GetHeight() double totalHeight = lastPosition.Value.V + maxV; if (calculateAvergae) { - return (totalHeight / _items.Count) * _context.ItemCount; + return (totalHeight / itemCount) * _context.ItemCount; } else { From 4d911e2d08d47f5b2ebdd7a56c9a6c74acbd2461 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 18 Feb 2020 16:21:16 -0700 Subject: [PATCH 063/105] Fix type in variable --- .../WrapLayout/WrapLayoutState.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index 1ce03ca6ab2..ede62583e38 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -93,10 +93,10 @@ internal double GetHeight() return 0; } - bool calculateAvergae = true; + bool calculateAverage = true; if ((_items.Count == _context.ItemCount) && _items[_items.Count - 1].Position.HasValue) { - calculateAvergae = false; + calculateAverage = false; } UvMeasure? lastPosition = null; @@ -126,7 +126,7 @@ internal double GetHeight() } double totalHeight = lastPosition.Value.V + maxV; - if (calculateAvergae) + if (calculateAverage) { return (totalHeight / itemCount) * _context.ItemCount; } From e09227bd39114b5568d5a43bbfd208486e872af5 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 19 Feb 2020 08:39:41 -0700 Subject: [PATCH 064/105] Make sure elements get recycled when they are outside the scope of the bounds. Move getting the elements to the layout --- .../StaggeredLayout/StaggeredItem.cs | 37 +------------------ .../StaggeredLayout/StaggeredLayout.cs | 18 +++++++-- .../StaggeredLayout/StaggeredLayoutState.cs | 2 +- 3 files changed, 16 insertions(+), 41 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs index 54e58872e6b..62a948c3e75 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -11,12 +11,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { internal class StaggeredItem { - private VirtualizingLayoutContext _context; - private Size? _size; - - public StaggeredItem(VirtualizingLayoutContext context, int index) + public StaggeredItem(int index) { - _context = context; this.Index = index; } @@ -25,36 +21,5 @@ public StaggeredItem(VirtualizingLayoutContext context, int index) public double Height { get; internal set; } public int Index { get; } - - internal Size Measure(double columnWidth, double availableHeight) - { - if (_size == null) - { - UIElement element = GetElement(); - - element.Measure(new Size(columnWidth, availableHeight)); - _size = element.DesiredSize; - Height = _size.Value.Height; - } - - return _size.Value; - } - - private UIElement GetElement() - { - return _context.GetOrCreateElementAt(Index); - } - - internal void Arrange(Rect bounds) - { - UIElement element = GetElement(); - element.Arrange(bounds); - } - - internal void RecycleElement() - { - UIElement element = GetElement(); - _context.RecycleElement(element); - } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index fab0e47c779..bb9750bb5ac 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -171,12 +171,18 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { var columnIndex = GetColumnIndex(columnHeights); + UIElement element = null; StaggeredItem item = state.GetItemAt(i); - Size elementSize = item.Measure(state.ColumnWidth, availableHeight); + if (item.Height == 0) + { + element = context.GetOrCreateElementAt(i); + element.Measure(new Size(state.ColumnWidth, availableHeight)); + item.Height = element.DesiredSize.Height; + } double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; item.Top = columnHeights[columnIndex] + spacing; - double bottom = item.Top + elementSize.Height; + double bottom = item.Top + item.Height; columnHeights[columnIndex] = bottom; itemsPerColumn[columnIndex]++; state.AddItemToColumn(item, columnIndex); @@ -184,7 +190,10 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (bottom < context.RealizationRect.Top) { // The bottom of the element is above the realization area - // item.RecycleElement(); + if (element != null) + { + context.RecycleElement(element); + } } else if (item.Top > context.RealizationRect.Bottom) { @@ -239,7 +248,8 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); Rect bounds = new Rect(itemHorizontalOffset, item.Top, state.ColumnWidth, item.Height); - item.Arrange(bounds); + UIElement element = context.GetOrCreateElementAt(item.Index); + element.Arrange(bounds); } else { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 274098669c5..f79968efdc7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -52,7 +52,7 @@ internal StaggeredItem GetItemAt(int index) } else { - StaggeredItem item = new StaggeredItem(_context, index); + StaggeredItem item = new StaggeredItem(index); _items.Add(item); return item; } From 592a645b0b94d9d6c65174478c88c45d2eac6e16 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 19 Feb 2020 09:34:24 -0700 Subject: [PATCH 065/105] Ignore row spacing for determining which column an items goes to --- .../StaggeredLayout/StaggeredLayout.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index bb9750bb5ac..f4a8a0245b8 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -180,10 +180,14 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.Height = element.DesiredSize.Height; } - double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; + double numberOfItems = itemsPerColumn[columnIndex]; + double spacing = numberOfItems > 0 ? (RowSpacing * numberOfItems) : 0; item.Top = columnHeights[columnIndex] + spacing; double bottom = item.Top + item.Height; - columnHeights[columnIndex] = bottom; + + // ignore row spacing. columnHeights is just used to determine which column to place the item. + // row spacing shouldn't change the column the item is within. + columnHeights[columnIndex] += item.Height; itemsPerColumn[columnIndex]++; state.AddItemToColumn(item, columnIndex); From 4118bdd5b9d7cec2823f17ed88611a67f5379721 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 19 Feb 2020 09:34:51 -0700 Subject: [PATCH 066/105] This array holds ints, not doubles --- .../StaggeredLayout/StaggeredLayout.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index f4a8a0245b8..150e02fd65a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -164,7 +164,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size } var columnHeights = new double[numColumns]; - var itemsPerColumn = new double[numColumns]; + var itemsPerColumn = new int[numColumns]; var deadColumns = new HashSet(); for (int i = 0; i < context.ItemCount; i++) @@ -180,7 +180,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.Height = element.DesiredSize.Height; } - double numberOfItems = itemsPerColumn[columnIndex]; + int numberOfItems = itemsPerColumn[columnIndex]; double spacing = numberOfItems > 0 ? (RowSpacing * numberOfItems) : 0; item.Top = columnHeights[columnIndex] + spacing; double bottom = item.Top + item.Height; From 1ed9fa496309d77968241e837a92218e80e3e589 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 19 Feb 2020 10:37:34 -0700 Subject: [PATCH 067/105] Add height to the debugger display --- .../StaggeredLayout/StaggeredColumnLayout.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs index b62ba1cb80c..5d3ce1f9b58 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredColumnLayout.cs @@ -6,6 +6,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { + [System.Diagnostics.DebuggerDisplay("Count = {Count}, Height = {Height}")] internal class StaggeredColumnLayout : List { public double Height { get; private set; } From e87a85bbd6ab92770038b50739e2b84183e48629 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 19 Feb 2020 10:39:04 -0700 Subject: [PATCH 068/105] We need to recalculate the height when the RowSpacing changes --- .../StaggeredLayout/StaggeredLayout.cs | 9 +++++++++ .../StaggeredLayout/StaggeredLayoutState.cs | 2 ++ 2 files changed, 11 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 150e02fd65a..3688f3a0d78 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -163,6 +163,15 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size state.ClearColumns(); } + if (RowSpacing != state.RowSpacing) + { + // If the RowSpacing changes the height of the rows will be different. + // The columns stores the height so we'll want to clear them out to + // get the proper height + state.ClearColumns(); + state.RowSpacing = RowSpacing; + } + var columnHeights = new double[numColumns]; var itemsPerColumn = new int[numColumns]; var deadColumns = new HashSet(); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index f79968efdc7..f0a5b39a644 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -25,6 +25,8 @@ public StaggeredLayoutState(VirtualizingLayoutContext context) public int NumberOfColumns { get { return _columnLayout.Count; } } + public double RowSpacing { get; internal set; } + internal void AddItemToColumn(StaggeredItem item, int columnIndex) { if (_columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout) == false) From 4ea0adaed74e4841809d5922867a21280f5c4619 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 19 Feb 2020 12:58:09 -0700 Subject: [PATCH 069/105] Revert the change to not account for row spacing when finding the column. Items will jump around when playing with RowSpacing but that's not a common use case and items were not placed in the right column --- .../StaggeredLayout/StaggeredLayout.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 3688f3a0d78..34b34e0218e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -189,14 +189,10 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.Height = element.DesiredSize.Height; } - int numberOfItems = itemsPerColumn[columnIndex]; - double spacing = numberOfItems > 0 ? (RowSpacing * numberOfItems) : 0; + double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; item.Top = columnHeights[columnIndex] + spacing; double bottom = item.Top + item.Height; - - // ignore row spacing. columnHeights is just used to determine which column to place the item. - // row spacing shouldn't change the column the item is within. - columnHeights[columnIndex] += item.Height; + columnHeights[columnIndex] = bottom; itemsPerColumn[columnIndex]++; state.AddItemToColumn(item, columnIndex); From 26ba3034758834f95aac9d371e608443b9c8015c Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 20 Feb 2020 11:46:28 -0700 Subject: [PATCH 070/105] Use the laest version of WinUI --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 2 +- .../Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index d95e24c902f..00f2935b1f9 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -125,7 +125,7 @@ 6.1.0-build.6 - 2.2.190917002 + 2.3.200213001 0.7.0-alpha diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj index 244df1c9e84..1b8cc6d8665 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj @@ -15,7 +15,7 @@ - + From 9e7b4427d44a5bdde5486defb60d2a504100d15a Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 20 Feb 2020 11:46:45 -0700 Subject: [PATCH 071/105] Make sure elements that are above the bounds are recycled --- .../WrapLayout/WrapLayout.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index c901241c1d4..0121029cc44 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -153,11 +153,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double currentV = 0; for (int i = 0; i < context.ItemCount; i++) { + UIElement child = null; bool measured = false; WrapItem item = state.GetItemAt(i); if (item.Measure == null) { - UIElement child = context.GetOrCreateElementAt(i); + child = context.GetOrCreateElementAt(i); child.Measure(availableSize); item.Measure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); measured = true; @@ -188,6 +189,10 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (vEnd < realizationBounds.VMin) { // Item is "above" the bounds + if (child != null) + { + context.RecycleElement(child); + } } else if (position.V > realizationBounds.VMax) { @@ -197,7 +202,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size else if (measured == false) { // Always measure elements that are within the bounds - UIElement child = context.GetOrCreateElementAt(i); + child = context.GetOrCreateElementAt(i); child.Measure(availableSize); } From 3e70dffb0ea921d52997cb0ad3213af3b5c5a0ab Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:17:06 -0600 Subject: [PATCH 072/105] Recycle an element if it is below the bounds --- .../StaggeredLayout/StaggeredLayout.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 34b34e0218e..a0ca4e9329f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -207,7 +207,10 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size else if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area - // item.RecycleElement(); + if (element != null) + { + context.RecycleElement(element); + } deadColumns.Add(columnIndex); } else From 4f3d48430316d8894e8152d2974292d7d7e641af Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:18:01 -0600 Subject: [PATCH 073/105] Make method static --- .../StaggeredLayout/StaggeredLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index a0ca4e9329f..1b2b34ea7fe 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -285,7 +285,7 @@ private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChang panel.InvalidateMeasure(); } - private int GetColumnIndex(double[] columnHeights) + private static int GetColumnIndex(double[] columnHeights) { int columnIndex = 0; double height = columnHeights[0]; From 37d8f221e5e6b7bece2f6c50540017b2f3112b27 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:25:03 -0600 Subject: [PATCH 074/105] Add case handling for items being replaced --- .../StaggeredLayout/StaggeredLayout.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 1b2b34ea7fe..7d65edd267a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -114,6 +114,10 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob { state.Clear(); } + else if (args.Action == NotifyCollectionChangedAction.Replace) + { + state.RemoveFromIndex(args.NewStartingIndex); + } base.OnItemsChangedCore(context, source, args); } From 63e571f5ca3e16e48d0b98c30565e13c523435ec Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:26:08 -0600 Subject: [PATCH 075/105] Switch to a switch statement --- .../StaggeredLayout/StaggeredLayout.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 7d65edd267a..d33deeda401 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -102,21 +102,20 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob { var state = (StaggeredLayoutState)context.LayoutState; - if (args.Action == NotifyCollectionChangedAction.Remove) + switch (args.Action) { - state.RemoveFromIndex(args.OldStartingIndex); - } - else if (args.Action == NotifyCollectionChangedAction.Add) - { - state.RemoveFromIndex(args.NewStartingIndex); - } - else if (args.Action == NotifyCollectionChangedAction.Reset) - { - state.Clear(); - } - else if (args.Action == NotifyCollectionChangedAction.Replace) - { - state.RemoveFromIndex(args.NewStartingIndex); + case NotifyCollectionChangedAction.Add: + case NotifyCollectionChangedAction.Replace: + state.RemoveFromIndex(args.NewStartingIndex); + break; + case NotifyCollectionChangedAction.Move: + break; + case NotifyCollectionChangedAction.Remove: + state.RemoveFromIndex(args.OldStartingIndex); + break; + case NotifyCollectionChangedAction.Reset: + state.Clear(); + break; } base.OnItemsChangedCore(context, source, args); From b00beff14c0def444bd0eca1bef3fae5001d2d79 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:29:36 -0600 Subject: [PATCH 076/105] Add a comment as to what is happening when the height is zero --- .../StaggeredLayout/StaggeredLayout.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index d33deeda401..f694802580d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -187,6 +187,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size StaggeredItem item = state.GetItemAt(i); if (item.Height == 0) { + // Item has not been measured yet. Get the element and store the values element = context.GetOrCreateElementAt(i); element.Measure(new Size(state.ColumnWidth, availableHeight)); item.Height = element.DesiredSize.Height; From c657716025dcb802b5baa8be4ad3ba084a9d0af1 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:29:49 -0600 Subject: [PATCH 077/105] Add required space after brace --- .../StaggeredLayout/StaggeredLayout.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index f694802580d..3c4ea762a31 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -215,6 +215,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { context.RecycleElement(element); } + deadColumns.Add(columnIndex); } else From 19f9fb021613030c1a40293e9043155c8409bc58 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:38:26 -0600 Subject: [PATCH 078/105] Handle moving of items --- .../StaggeredLayout/StaggeredLayout.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 3c4ea762a31..122aa30803f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -109,6 +109,8 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob state.RemoveFromIndex(args.NewStartingIndex); break; case NotifyCollectionChangedAction.Move: + int index = Math.Min(args.NewStartingIndex, args.OldStartingIndex); + state.RemoveFromIndex(index); break; case NotifyCollectionChangedAction.Remove: state.RemoveFromIndex(args.OldStartingIndex); From d1c12a58b5c79d54d651326627839f52c26771e1 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:49:11 -0600 Subject: [PATCH 079/105] Recycle elements when they are replaced or moved to ensure the correct context --- .../StaggeredLayout/StaggeredLayout.cs | 17 +++++++++++++++-- .../StaggeredLayout/StaggeredLayoutState.cs | 7 +++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 122aa30803f..2d2a760fa22 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -105,12 +105,25 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob switch (args.Action) { case NotifyCollectionChangedAction.Add: + state.RemoveFromIndex(args.NewStartingIndex); + break; case NotifyCollectionChangedAction.Replace: state.RemoveFromIndex(args.NewStartingIndex); + + // We must recycle the element to ensure that it gets the correct context + state.RecycleElementAt(args.NewStartingIndex); break; case NotifyCollectionChangedAction.Move: - int index = Math.Min(args.NewStartingIndex, args.OldStartingIndex); - state.RemoveFromIndex(index); + int minIndex = Math.Min(args.NewStartingIndex, args.OldStartingIndex); + int maxIndex = Math.Max(args.NewStartingIndex, args.OldStartingIndex); + state.RemoveFromIndex(minIndex); + + // We must recycle all elements to ensure that it gets the correct context + for (int i = minIndex; i <= maxIndex; i++) + { + state.RecycleElementAt(i); + } + break; case NotifyCollectionChangedAction.Remove: state.RemoveFromIndex(args.OldStartingIndex); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index f0a5b39a644..6ce0eb64689 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.UI.Xaml.Controls; +using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -123,6 +124,12 @@ internal double GetHeight() return desiredHeight; } + internal void RecycleElementAt(int index) + { + UIElement element = _context.GetOrCreateElementAt(index); + _context.RecycleElement(element); + } + internal void RemoveFromIndex(int index) { if (index > _items.Count) From 74c5ede33c76286ac227a831c7046f514e0fe620 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 11:52:32 -0600 Subject: [PATCH 080/105] Switch to a swtich statement for the items changing --- .../WrapLayout/WrapLayout.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 0121029cc44..3819c61bb77 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -107,17 +107,21 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob { var state = (WrapLayoutState)context.LayoutState; - if (args.Action == NotifyCollectionChangedAction.Remove) + switch (args.Action) { - state.RemoveFromIndex(args.OldStartingIndex); - } - else if (args.Action == NotifyCollectionChangedAction.Add) - { - state.RemoveFromIndex(args.NewStartingIndex); - } - else if (args.Action == NotifyCollectionChangedAction.Reset) - { - state.Clear(); + case NotifyCollectionChangedAction.Add: + state.RemoveFromIndex(args.NewStartingIndex); + break; + case NotifyCollectionChangedAction.Move: + break; + case NotifyCollectionChangedAction.Remove: + state.RemoveFromIndex(args.OldStartingIndex); + break; + case NotifyCollectionChangedAction.Replace: + break; + case NotifyCollectionChangedAction.Reset: + state.Clear(); + break; } base.OnItemsChangedCore(context, source, args); From 978e3dbce8ec7f448e2c7c635091f447d8d75236 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 13:07:44 -0600 Subject: [PATCH 081/105] Add support for moving an item in the wrap layout --- .../WrapLayout/WrapLayout.cs | 3 +++ .../WrapLayout/WrapLayoutState.cs | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 3819c61bb77..ee8f29bf2f9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -113,6 +113,9 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob state.RemoveFromIndex(args.NewStartingIndex); break; case NotifyCollectionChangedAction.Move: + int minIndex = Math.Min(args.NewStartingIndex, args.OldStartingIndex); + int maxIndex = Math.Max(args.NewStartingIndex, args.OldStartingIndex); + state.RemoveRange(minIndex, maxIndex); break; case NotifyCollectionChangedAction.Remove: state.RemoveFromIndex(args.OldStartingIndex); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index ede62583e38..ea640a4d753 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.UI.Xaml.Controls; +using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls @@ -62,6 +63,24 @@ internal void RemoveFromIndex(int index) _items.RemoveRange(index, numToRemove); } + internal void RemoveRange(int startIndex, int endIndex) + { + for (int i = startIndex; i <= endIndex; i++) + { + if (i > _items.Count) + { + return; + } + + WrapItem item = _items[i]; + item.Measure = null; + item.Position = null; + + // We must recycle all elements to ensure that they get the correct context + RecycleElementAt(i); + } + } + internal void SetOrientation(Orientation orientation) { foreach (var item in _items.Where(i => i.Measure.HasValue)) @@ -135,5 +154,11 @@ internal double GetHeight() return totalHeight; } } + + internal void RecycleElementAt(int index) + { + UIElement element = _context.GetOrCreateElementAt(index); + _context.RecycleElement(element); + } } } \ No newline at end of file From 9b55f99d31e130dae183a8e06c194604b1e511a9 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 13:13:31 -0600 Subject: [PATCH 082/105] Do not clear the items from the collection. zero out their measure values so they get recalculated --- .../StaggeredLayout/StaggeredLayout.cs | 9 +----- .../StaggeredLayout/StaggeredLayoutState.cs | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 2d2a760fa22..feed842efbd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -116,14 +116,7 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob case NotifyCollectionChangedAction.Move: int minIndex = Math.Min(args.NewStartingIndex, args.OldStartingIndex); int maxIndex = Math.Max(args.NewStartingIndex, args.OldStartingIndex); - state.RemoveFromIndex(minIndex); - - // We must recycle all elements to ensure that it gets the correct context - for (int i = minIndex; i <= maxIndex; i++) - { - state.RecycleElementAt(i); - } - + state.RemoveRange(minIndex, maxIndex); break; case NotifyCollectionChangedAction.Remove: state.RemoveFromIndex(args.OldStartingIndex); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index 6ce0eb64689..e3f4842aaad 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -155,5 +155,37 @@ internal void RemoveFromIndex(int index) } } } + + internal void RemoveRange(int startIndex, int endIndex) + { + for (int i = startIndex; i <= endIndex; i++) + { + if (i > _items.Count) + { + break; + } + + StaggeredItem item = _items[i]; + item.Height = 0; + item.Top = 0; + + // We must recycle all elements to ensure that it gets the correct context + RecycleElementAt(i); + } + + foreach (var kvp in _columnLayout) + { + StaggeredColumnLayout layout = kvp.Value; + for (int i = 0; i < layout.Count; i++) + { + if ((startIndex <= layout[i].Index) && (layout[i].Index <= endIndex)) + { + int numToRemove = layout.Count - i; + layout.RemoveRange(i, numToRemove); + break; + } + } + } + } } } From da95ec757c6ecf4c476c44c607ea5c5538924b65 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 13:49:38 -0600 Subject: [PATCH 083/105] Add handling for an item being replaced in the wrap layout --- .../WrapLayout/WrapLayout.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index ee8f29bf2f9..79233b8a8ce 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -121,6 +121,8 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob state.RemoveFromIndex(args.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: + state.RemoveFromIndex(args.NewStartingIndex); + state.RecycleElementAt(args.NewStartingIndex); break; case NotifyCollectionChangedAction.Reset: state.Clear(); From 5d7164f0294153862f7e99df5b5ffa3f5cc4aebb Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Mon, 9 Mar 2020 13:56:14 -0600 Subject: [PATCH 084/105] A move could effect the position of items after it. So just clear them out --- .../WrapLayout/WrapLayout.cs | 6 ++++-- .../WrapLayout/WrapLayoutState.cs | 18 ------------------ 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 79233b8a8ce..e64432e3f4f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -114,8 +114,10 @@ protected override void OnItemsChangedCore(VirtualizingLayoutContext context, ob break; case NotifyCollectionChangedAction.Move: int minIndex = Math.Min(args.NewStartingIndex, args.OldStartingIndex); - int maxIndex = Math.Max(args.NewStartingIndex, args.OldStartingIndex); - state.RemoveRange(minIndex, maxIndex); + state.RemoveFromIndex(minIndex); + + state.RecycleElementAt(args.OldStartingIndex); + state.RecycleElementAt(args.NewStartingIndex); break; case NotifyCollectionChangedAction.Remove: state.RemoveFromIndex(args.OldStartingIndex); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index ea640a4d753..e88e72bbc15 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -63,24 +63,6 @@ internal void RemoveFromIndex(int index) _items.RemoveRange(index, numToRemove); } - internal void RemoveRange(int startIndex, int endIndex) - { - for (int i = startIndex; i <= endIndex; i++) - { - if (i > _items.Count) - { - return; - } - - WrapItem item = _items[i]; - item.Measure = null; - item.Position = null; - - // We must recycle all elements to ensure that they get the correct context - RecycleElementAt(i); - } - } - internal void SetOrientation(Orientation orientation) { foreach (var item in _items.Where(i => i.Measure.HasValue)) From 0338d70667975c8c604228e4de6e924a4ca28146 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 10 Mar 2020 12:06:29 -0600 Subject: [PATCH 085/105] Add header to file --- .../WrapLayout/UvMeasure.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs index c59f9c859ca..9aca6edf8c5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/UvMeasure.cs @@ -1,4 +1,8 @@ -using Windows.UI.Xaml.Controls; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -34,5 +38,10 @@ public override bool Equals(object obj) return false; } + + public override int GetHashCode() + { + return base.GetHashCode(); + } } } From 6a02450575706e3b106968c2ed5fea53547c7ab5 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 10 Mar 2020 12:06:37 -0600 Subject: [PATCH 086/105] Remove unused usings --- .../WrapLayout/WrapLayout.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index e64432e3f4f..750e3964d96 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -3,11 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Specialized; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.UI.Xaml.Controls; using Windows.Foundation; using Windows.UI.Xaml; From 15e924fe8554a210871edf383b9bde4b52884944 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 10 Mar 2020 12:12:42 -0600 Subject: [PATCH 087/105] Add summary comment for the class --- .../WrapLayout/WrapLayout.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 750e3964d96..e61c4b57706 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -11,6 +11,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// Arranges elements by wrapping them to fit the available space. + /// When is set to Orientation.Horizontal, element are arranged in rows until the available width is reached and then to a new row. + /// When is set to Orientation.Vertical, element are arranged in columns until the available height is reached. + /// public class WrapLayout : VirtualizingLayout { /// From bf7c0839e1c2a2ce1c4550ec85ae0585e9692279 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 10 Mar 2020 15:31:39 -0600 Subject: [PATCH 088/105] Add a simple WrapLayout sample --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 14 +++++ .../SamplePages/WrapLayout/WrapLayout.bind | 33 +++++++++++ .../SamplePages/WrapLayout/WrapLayout.png | Bin 0 -> 891 bytes .../WrapLayout/WrapLayoutPage.xaml | 15 +++++ .../WrapLayout/WrapLayoutPage.xaml.cs | 56 ++++++++++++++++++ .../SamplePages/samples.json | 12 +++- ...soft.Toolkit.Uwp.UI.Controls.Layout.csproj | 2 +- 7 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.png create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 00f2935b1f9..c345e70f3de 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -380,6 +380,7 @@ + @@ -542,6 +543,9 @@ TokenizingTextBoxPage.xaml + + WrapLayoutPage.xaml + @@ -552,6 +556,7 @@ + @@ -1330,6 +1335,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -1403,6 +1412,10 @@ {daeb9cec-c817-33b2-74b2-bc379380db72} Microsoft.Toolkit.Uwp.UI.Controls.DataGrid + + {cb444381-18ba-4a51-bb32-3a498bcc1e99} + Microsoft.Toolkit.Uwp.UI.Controls.Layout + {e9faabfb-d726-42c1-83c1-cb46a29fea81} Microsoft.Toolkit.Uwp.UI.Controls @@ -1436,6 +1449,7 @@ Visual C++ 2015 Runtime for Universal Windows Platform Apps + 14.0 diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind new file mode 100644 index 00000000000..0707029938e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.png new file mode 100644 index 0000000000000000000000000000000000000000..5e7ae5ab2d18d72885ae30ba4cd0f415f4bd6cec GIT binary patch literal 891 zcmeAS@N?(olHy`uVBq!ia0y~yU{nCI4{)#n$t4^1Z39w_#X;^)4C~IxyaaMs(j9#r z85lP9bN@+X1@buyJR*x382Ao@Fyrz36)6l1%mSV+jv*CsZ|_*=t#)8=2>fIA@R04^ zqyMHKV0#f;AznD)vihcnYI};82+lvm+Ob;GrdX=&;V%h>-M_gm)b3@vasKE1&nyY$ zOe`rZ949ygCI~7hC_6Z~H8k`vFiJ8q8385CBD)Ox9>1;Rk~y+TXZrJ(yCb?7OU&jn z<_7pAq)XSGPjhf^M^=y1pt`qNKQd?iTW#C=tgBi?@jptqe9?8ouGYM`PPR)Z* zV)^kMcE@V>R!uIH$)A5bHB#?2?}PKVK2+PyW`u + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs new file mode 100644 index 00000000000..97b27f8d55b --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.ObjectModel; +using Microsoft.Toolkit.Uwp.UI.Extensions; +using Microsoft.UI.Xaml.Controls; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class WrapLayoutPage : Page, IXamlRenderListener + { + private ObservableCollection _items = new ObservableCollection(); + private Random _random; + + public WrapLayoutPage() + { + this.InitializeComponent(); + + _random = new Random(DateTime.Now.Millisecond); + for (int i = 0; i < _random.Next(1000, 5000); i++) + { + var item = new Item { Index = i, Width = _random.Next(50, 250), Height = _random.Next(50, 250), Color = Color.FromArgb(255, (byte)_random.Next(0, 255), (byte)_random.Next(0, 255), (byte)_random.Next(0, 255)) }; + _items.Add(item); + } + } + + public void OnXamlRendered(FrameworkElement control) + { + var repeater = control.FindChildByName("WrapRepeater") as ItemsRepeater; + + if (repeater != null) + { + repeater.ItemsSource = _items; + } + } + + private class Item + { + public int Index { get; internal set; } + + public int Width { get; internal set; } + + public int Height { get; internal set; } + + public Color Color { get; internal set; } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 22cef444cca..0990a8914a3 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -229,6 +229,16 @@ "Icon": "/SamplePages/WrapPanel/WrapPanel.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/WrapPanel.md" }, + { + "Name": "WrapLayout", + "Type": "WrapLayoutPage", + "Subcategory": "Layout", + "About": "The WrapLayout virtualizes child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box.", + //"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout", + "XamlCodeFile": "WrapLayout.bind", + "Icon": "/SamplePages/WrapLayout/WrapLayout.png" + //"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/Layout/WrapLayout.md" + }, { "Name": "OrbitView", "Type": "OrbitViewPage", @@ -424,7 +434,7 @@ "XamlCodeFile": "FocusTrackerXaml.bind", "Icon": "/SamplePages/FocusTracker/FocusTracker.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/developer-tools/FocusTracker.md" - }//, + } //, //{ // "Name": "TokenizingTextBox", // "Type": "TokenizingTextBoxPage", diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj index 1b8cc6d8665..b1f67ff8750 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Microsoft.Toolkit.Uwp.UI.Controls.Layout.csproj @@ -1,7 +1,7 @@  - uap10.0.18362 + uap10.0.17134 10.0.18362.0 Windows Community Toolkit Layout From be13161311a62eedea4c0ea62889eb00b5c25dc6 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 10 Mar 2020 15:45:14 -0600 Subject: [PATCH 089/105] Add a simple sample for the new staggered layout --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 9 +++ .../StaggeredLayout/StaggeredLayout.bind | 35 +++++++++++ .../StaggeredLayout/StaggeredLayout.png | Bin 0 -> 196 bytes .../StaggeredLayout/StaggeredLayoutPage.xaml | 14 +++++ .../StaggeredLayoutPage.xaml.cs | 56 ++++++++++++++++++ .../SamplePages/samples.json | 10 ++++ 6 files changed, 124 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.bind create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.png create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index c345e70f3de..127f2ad066f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -286,6 +286,7 @@ + @@ -539,6 +540,9 @@ ImageCropperPage.xaml + + StaggeredLayoutPage.xaml + TokenizingTextBoxPage.xaml @@ -557,6 +561,7 @@ + @@ -959,6 +964,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.bind new file mode 100644 index 00000000000..089b5918bb1 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.bind @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayout.png new file mode 100644 index 0000000000000000000000000000000000000000..08ab2b77ce7822db0c12a18d0800ccfa91d81b42 GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0y~yU{nCI4=^$V$$51;Ux5@`fKP}kkXBPu>nLe;0kRlN zg8YJ|_O>X>0696HE{-7;ac{33D; zo~H}%dzF>(+uHQ{HuffGJ(^M9t-#TA>AW<@DcP_;QmrB-`)a-OK7R>qSimovB*0SH r=1}^kDdwog_WBDpT?!mp{*%HkJ6yiBqUlTw&|U^lS3j3^P6 + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs new file mode 100644 index 00000000000..7b62a3b8e2a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.ObjectModel; +using Microsoft.Toolkit.Uwp.UI.Extensions; +using Microsoft.UI.Xaml.Controls; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class StaggeredLayoutPage : Page, IXamlRenderListener + { + private ObservableCollection _items = new ObservableCollection(); + private Random _random; + + public StaggeredLayoutPage() + { + this.InitializeComponent(); + + _random = new Random(DateTime.Now.Millisecond); + for (int i = 0; i < _random.Next(1000, 5000); i++) + { + var item = new Item { Index = i, Width = _random.Next(50, 250), Height = _random.Next(50, 250), Color = Color.FromArgb(255, (byte)_random.Next(0, 255), (byte)_random.Next(0, 255), (byte)_random.Next(0, 255)) }; + _items.Add(item); + } + } + + public void OnXamlRendered(FrameworkElement control) + { + var repeater = control.FindChildByName("StaggeredRepeater") as ItemsRepeater; + + if (repeater != null) + { + repeater.ItemsSource = _items; + } + } + + private class Item + { + public int Index { get; internal set; } + + public int Width { get; internal set; } + + public int Height { get; internal set; } + + public Color Color { get; internal set; } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 0990a8914a3..2b01a03f7ac 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -312,6 +312,16 @@ "Icon": "/SamplePages/StaggeredPanel/StaggeredPanel.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/StaggeredPanel.md" }, + { + "Name": "StaggeredLayout", + "Type": "StaggeredLayoutPage", + "Subcategory": "Layout", + "About": "The StaggeredLayout virtualizes items in a column approach where an item will be added to whichever column has used the least amount of space.", + //"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout", + "XamlCodeFile": "StaggeredLayout.bind", + "Icon": "/SamplePages/StaggeredLayout/StaggeredLayout.png" + //"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/layout/StaggeredLayout.md" + }, { "Name": "LayoutTransformControl", "Type": "LayoutTransformControlPage", From 28e9c545d46c4d40390c39e6beb398ba52fed828 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 12 Mar 2020 16:15:11 -0600 Subject: [PATCH 090/105] Put the property on different lines --- .../StaggeredLayout/StaggeredLayoutState.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index e3f4842aaad..adbaa06900e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -24,7 +24,13 @@ public StaggeredLayoutState(VirtualizingLayoutContext context) public double ColumnWidth { get; internal set; } - public int NumberOfColumns { get { return _columnLayout.Count; } } + public int NumberOfColumns + { + get + { + return _columnLayout.Count; + } + } public double RowSpacing { get; internal set; } From cefad16e56c05b479cb910433e17d05ebf493c6e Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 12 Mar 2020 16:15:56 -0600 Subject: [PATCH 091/105] Remove trailing white space --- .../WrapLayout/WrapLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index e61c4b57706..928c70025f9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -12,7 +12,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { /// - /// Arranges elements by wrapping them to fit the available space. + /// Arranges elements by wrapping them to fit the available space. /// When is set to Orientation.Horizontal, element are arranged in rows until the available width is reached and then to a new row. /// When is set to Orientation.Vertical, element are arranged in columns until the available height is reached. /// From 841a5a47f920f9eb06b4711a8b3fe086d9c9c87a Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Fri, 13 Mar 2020 08:21:14 -0600 Subject: [PATCH 092/105] Remove empty Item group --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 127f2ad066f..4a725a20ec0 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -1458,7 +1458,6 @@ Visual C++ 2015 Runtime for Universal Windows Platform Apps - 14.0 From 85a200342a060df0f838411e216bd981f641ad95 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Fri, 13 Mar 2020 08:26:47 -0600 Subject: [PATCH 093/105] recycle elements that are below the bounds --- .../WrapLayout/WrapLayout.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 928c70025f9..94490732be3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -209,6 +209,12 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size else if (position.V > realizationBounds.VMax) { // Item is "below" the bounds. + if (child != null) + { + context.RecycleElement(child); + } + + // We don't need to measure anything below the bounds break; } else if (measured == false) From e22ac0e8dbeadb6ba339a4ce76eb16cf57595be4 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 2 Apr 2020 17:02:44 -0600 Subject: [PATCH 094/105] Rename local method --- .../WrapLayout/WrapLayout.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 94490732be3..df0f9af3b25 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -251,7 +251,7 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var state = (WrapLayoutState)context.LayoutState; - bool arrange(WrapItem item, bool isLast = false) + bool Arrange(WrapItem item, bool isLast = false) { if (item.Measure.HasValue == false) { @@ -300,7 +300,7 @@ bool arrange(WrapItem item, bool isLast = false) for (var i = 0; i < context.ItemCount; i++) { - bool continueArranging = arrange(state.GetItemAt(i)); + bool continueArranging = Arrange(state.GetItemAt(i)); if (continueArranging == false) { break; From 2dbf1362b50ed069f21ab76fafce2cb7f6114de9 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 14 Apr 2020 11:02:03 -0600 Subject: [PATCH 095/105] Only measure an item if it wasn't already measured. This helps speed up items when first being measured --- .../StaggeredLayout/StaggeredLayout.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index feed842efbd..1e3a990a929 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -191,6 +191,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size { var columnIndex = GetColumnIndex(columnHeights); + bool measured = false; UIElement element = null; StaggeredItem item = state.GetItemAt(i); if (item.Height == 0) @@ -199,6 +200,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size element = context.GetOrCreateElementAt(i); element.Measure(new Size(state.ColumnWidth, availableHeight)); item.Height = element.DesiredSize.Height; + measured = true; } double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; @@ -226,7 +228,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size deadColumns.Add(columnIndex); } - else + else if (measured == false) { // We ALWAYS want to measure an item that will be in the bounds context.GetOrCreateElementAt(i).Measure(new Size(state.ColumnWidth, availableHeight)); From d36645739d5a1a0038b41f9b337fdd7c2a434a30 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 14 Apr 2020 11:16:15 -0600 Subject: [PATCH 096/105] Increae the size of the items --- .../SamplePages/WrapLayout/WrapLayout.bind | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind index 0707029938e..18549c16f82 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayout.bind @@ -8,7 +8,7 @@ - + From 1880101ee3f5580c18757d1ec1c1f1a499c3e08f Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Tue, 14 Apr 2020 11:33:53 -0600 Subject: [PATCH 097/105] Add the location for code and documentation --- .../SamplePages/samples.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 7f73d79aac9..1c36431b830 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -234,10 +234,10 @@ "Type": "WrapLayoutPage", "Subcategory": "Layout", "About": "The WrapLayout virtualizes child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box.", - //"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout", + "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout", "XamlCodeFile": "WrapLayout.bind", - "Icon": "/SamplePages/WrapLayout/WrapLayout.png" - //"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/Layout/WrapLayout.md" + "Icon": "/SamplePages/WrapLayout/WrapLayout.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/layout/WrapLayout.md" }, { "Name": "OrbitView", @@ -317,10 +317,10 @@ "Type": "StaggeredLayoutPage", "Subcategory": "Layout", "About": "The StaggeredLayout virtualizes items in a column approach where an item will be added to whichever column has used the least amount of space.", - //"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout", + "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout", "XamlCodeFile": "StaggeredLayout.bind", - "Icon": "/SamplePages/StaggeredLayout/StaggeredLayout.png" - //"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/layout/StaggeredLayout.md" + "Icon": "/SamplePages/StaggeredLayout/StaggeredLayout.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/layout/StaggeredLayout.md" }, { "Name": "LayoutTransformControl", From d8d19186dd47cfea8dd1312744271975d71148e1 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Wed, 6 May 2020 12:01:28 -0600 Subject: [PATCH 098/105] Track the elements that are used and make sure they recycled when they are outside of the bounds of the realization area --- .../StaggeredLayout/StaggeredItem.cs | 2 ++ .../StaggeredLayout/StaggeredLayout.cs | 20 ++++++++++-------- .../WrapLayout/WrapItem.cs | 4 ++++ .../WrapLayout/WrapLayout.cs | 21 ++++++++++--------- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs index 62a948c3e75..c1bcac34815 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredItem.cs @@ -21,5 +21,7 @@ public StaggeredItem(int index) public double Height { get; internal set; } public int Index { get; } + + public UIElement Element { get; internal set; } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 1e3a990a929..3e327c717ff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -192,14 +192,13 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size var columnIndex = GetColumnIndex(columnHeights); bool measured = false; - UIElement element = null; StaggeredItem item = state.GetItemAt(i); if (item.Height == 0) { // Item has not been measured yet. Get the element and store the values - element = context.GetOrCreateElementAt(i); - element.Measure(new Size(state.ColumnWidth, availableHeight)); - item.Height = element.DesiredSize.Height; + item.Element = context.GetOrCreateElementAt(i); + item.Element.Measure(new Size(state.ColumnWidth, availableHeight)); + item.Height = item.Element.DesiredSize.Height; measured = true; } @@ -213,17 +212,19 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (bottom < context.RealizationRect.Top) { // The bottom of the element is above the realization area - if (element != null) + if (item.Element != null) { - context.RecycleElement(element); + context.RecycleElement(item.Element); + item.Element = null; } } else if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area - if (element != null) + if (item.Element != null) { - context.RecycleElement(element); + context.RecycleElement(item.Element); + item.Element = null; } deadColumns.Add(columnIndex); @@ -231,7 +232,8 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size else if (measured == false) { // We ALWAYS want to measure an item that will be in the bounds - context.GetOrCreateElementAt(i).Measure(new Size(state.ColumnWidth, availableHeight)); + item.Element = context.GetOrCreateElementAt(i); + item.Element.Measure(new Size(state.ColumnWidth, availableHeight)); } if (deadColumns.Count == numColumns) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs index 40f22a62dfc..ea5ac2f90f3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapItem.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Windows.UI.Xaml; + namespace Microsoft.Toolkit.Uwp.UI.Controls { internal class WrapItem @@ -16,5 +18,7 @@ public WrapItem(int index) public UvMeasure? Measure { get; internal set; } public UvMeasure? Position { get; internal set; } + + public UIElement Element { get; internal set; } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index df0f9af3b25..828f82195ee 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -165,14 +165,13 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size double currentV = 0; for (int i = 0; i < context.ItemCount; i++) { - UIElement child = null; bool measured = false; WrapItem item = state.GetItemAt(i); if (item.Measure == null) { - child = context.GetOrCreateElementAt(i); - child.Measure(availableSize); - item.Measure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); + item.Element = context.GetOrCreateElementAt(i); + item.Element.Measure(availableSize); + item.Measure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height); measured = true; } @@ -201,17 +200,19 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size if (vEnd < realizationBounds.VMin) { // Item is "above" the bounds - if (child != null) + if (item.Element != null) { - context.RecycleElement(child); + context.RecycleElement(item.Element); + item.Element = null; } } else if (position.V > realizationBounds.VMax) { // Item is "below" the bounds. - if (child != null) + if (item.Element != null) { - context.RecycleElement(child); + context.RecycleElement(item.Element); + item.Element = null; } // We don't need to measure anything below the bounds @@ -220,8 +221,8 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size else if (measured == false) { // Always measure elements that are within the bounds - child = context.GetOrCreateElementAt(i); - child.Measure(availableSize); + item.Element = context.GetOrCreateElementAt(i); + item.Element.Measure(availableSize); } position.U += currentMeasure.U + spacingMeasure.U; From a4e18e4da35e95fb05e000d98fbcdc560adaed15 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 08:34:29 -0600 Subject: [PATCH 099/105] Change the category for the layouts --- Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 20022a11b30..d440632a6d2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -232,7 +232,7 @@ { "Name": "WrapLayout", "Type": "WrapLayoutPage", - "Subcategory": "Layout", + "Subcategory": "Layout - ItemsRepeater", "About": "The WrapLayout virtualizes child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box.", "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout", "XamlCodeFile": "WrapLayout.bind", @@ -315,7 +315,7 @@ { "Name": "StaggeredLayout", "Type": "StaggeredLayoutPage", - "Subcategory": "Layout", + "Subcategory": "Layout - ItemsRepeater", "About": "The StaggeredLayout virtualizes items in a column approach where an item will be added to whichever column has used the least amount of space.", "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout", "XamlCodeFile": "StaggeredLayout.bind", From a3eef4a7ff8d5f19b86c32fca65c62879901232b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 08:50:53 -0600 Subject: [PATCH 100/105] When an item changes size we need to set a new height for the item and reset everything after it --- .../StaggeredLayout/StaggeredLayout.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs index 3e327c717ff..350c14be778 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayout.cs @@ -234,6 +234,13 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // We ALWAYS want to measure an item that will be in the bounds item.Element = context.GetOrCreateElementAt(i); item.Element.Measure(new Size(state.ColumnWidth, availableHeight)); + if (item.Height != item.Element.DesiredSize.Height) + { + // this item changed size; we need to recalculate layout for everything after this + state.RemoveFromIndex(i + 1); + item.Height = item.Element.DesiredSize.Height; + columnHeights[columnIndex] = item.Top + item.Height; + } } if (deadColumns.Count == numColumns) From 262275a196f099c4d98ab631b02816aef11ce769 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 08:56:44 -0600 Subject: [PATCH 101/105] If the current measure of the item does not match then the size of the item changed. We need to recalculate layout for everything after it --- .../WrapLayout/WrapLayout.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 828f82195ee..232fc539699 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -223,6 +223,14 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // Always measure elements that are within the bounds item.Element = context.GetOrCreateElementAt(i); item.Element.Measure(availableSize); + + currentMeasure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height); + if (item.Measure.Value.U != currentMeasure.U) + { + // this item changed size; we need to recalculate layout for everything after this + state.RemoveFromIndex(i + 1); + item.Measure = currentMeasure; + } } position.U += currentMeasure.U + spacingMeasure.U; From 92abf24ab84d4c5d22ca19cc3aa00f3f0fd1d34a Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 09:08:11 -0600 Subject: [PATCH 102/105] Need to check changes in V also --- .../WrapLayout/WrapLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 232fc539699..9cdde290d05 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -225,7 +225,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.Element.Measure(availableSize); currentMeasure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height); - if (item.Measure.Value.U != currentMeasure.U) + if ((item.Measure.Value.U != currentMeasure.U) || (item.Measure.Value.V != currentMeasure.V)) { // this item changed size; we need to recalculate layout for everything after this state.RemoveFromIndex(i + 1); From 25ed6d8b6ce7938be78acfcc610c66a2a507bdc0 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 09:09:37 -0600 Subject: [PATCH 103/105] Just use the handy Equals method! --- .../WrapLayout/WrapLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index 9cdde290d05..b981f250c4d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -225,7 +225,7 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size item.Element.Measure(availableSize); currentMeasure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height); - if ((item.Measure.Value.U != currentMeasure.U) || (item.Measure.Value.V != currentMeasure.V)) + if (currentMeasure.Equals(item.Measure) == false) { // this item changed size; we need to recalculate layout for everything after this state.RemoveFromIndex(i + 1); From c092cf7848c5d2e13417f9204e265cd0fcd1ab04 Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 09:43:53 -0600 Subject: [PATCH 104/105] No need to remove items that are above index --- .../StaggeredLayout/StaggeredLayoutState.cs | 2 +- .../WrapLayout/WrapLayoutState.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs index adbaa06900e..15ce0c0d0ce 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/StaggeredLayout/StaggeredLayoutState.cs @@ -138,7 +138,7 @@ internal void RecycleElementAt(int index) internal void RemoveFromIndex(int index) { - if (index > _items.Count) + if (index >= _items.Count) { // Item was added/removed but we haven't realized that far yet return; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs index e88e72bbc15..e9a55cfd40f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayoutState.cs @@ -53,7 +53,7 @@ internal void Clear() internal void RemoveFromIndex(int index) { - if (index > _items.Count) + if (index >= _items.Count) { // Item was added/removed but we haven't realized that far yet return; From d542dccd8ebe11bd1d8802af1e9ea4e1c459c85b Mon Sep 17 00:00:00 2001 From: Shawn Kendrot Date: Thu, 14 May 2020 09:44:16 -0600 Subject: [PATCH 105/105] Move items to a new row if the size makes them larger than the given space --- .../WrapLayout/WrapLayout.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs index b981f250c4d..7895892fe32 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/WrapLayout/WrapLayout.cs @@ -230,6 +230,17 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size // this item changed size; we need to recalculate layout for everything after this state.RemoveFromIndex(i + 1); item.Measure = currentMeasure; + + // did the change make it go into the new row? + if (parentMeasure.U < position.U + currentMeasure.U) + { + // New Row + position.U = 0; + position.V += currentV + spacingMeasure.V; + currentV = 0; + } + + item.Position = position; } }