From 4a637aa0738313587a8ef09cad6a25aa2a255e94 Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 9 Dec 2021 12:18:35 -0800
Subject: [PATCH 1/6] [WIP] Add Initial CanvasLayout for ItemsRepeater
Issues:
- Text isn't realized on first load if all items on screen (may be because we aren't getting/measuring containers in current measure step)
- Scrolling is getting locked trying to keep an item on screen, not sure what this may be (maybe not recycling containers?)
Need to try properly measuring/recylcing containers and see if that resolves any current issues...
---
.../CanvasLayout.Sample.csproj | 2 +
.../SampleOne/SamplePage.xaml.cs | 2 +-
.../SampleThree/SamplePage3.xaml | 39 +++++++
.../SampleThree/SamplePage3.xaml.cs | 62 ++++++++++
.../SampleTwo/SamplePage2.xaml.cs | 2 +-
Labs/CanvasLayout/src/CanvasLayout.cs | 110 ++++++++++++++++++
6 files changed, 215 insertions(+), 2 deletions(-)
create mode 100644 Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
create mode 100644 Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml.cs
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/CanvasLayout.Sample.csproj b/Labs/CanvasLayout/samples/CanvasLayout.Sample/CanvasLayout.Sample.csproj
index 935bb389d..e8f62ae6e 100644
--- a/Labs/CanvasLayout/samples/CanvasLayout.Sample/CanvasLayout.Sample.csproj
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/CanvasLayout.Sample.csproj
@@ -19,11 +19,13 @@
+
+
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleOne/SamplePage.xaml.cs b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleOne/SamplePage.xaml.cs
index b9e80ba2b..faaaa4411 100644
--- a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleOne/SamplePage.xaml.cs
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleOne/SamplePage.xaml.cs
@@ -51,7 +51,7 @@ namespace CanvasLayout.Sample.SampleOne
[ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Arial", value: "Arial")]
[ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Consolas", value: "Consolas")]
- [ToolkitSample(id: nameof(SamplePage), "Canvas Layout", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "A canvas-like VirtualizingPanel for use in an ItemsControl")]
+ [ToolkitSample(id: nameof(SamplePage), "Simple Options", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "A sample page for showing how to do simple options.")]
public sealed partial class SamplePage : Page
{
public SamplePage()
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
new file mode 100644
index 000000000..97bd1e84b
--- /dev/null
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml.cs b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml.cs
new file mode 100644
index 000000000..b9e80ba2b
--- /dev/null
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml.cs
@@ -0,0 +1,62 @@
+// 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.IO;
+using System.Linq;
+using CommunityToolkit.Labs.Core.SourceGenerators;
+using CommunityToolkit.Labs.Core.SourceGenerators.Attributes;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+#if !WINAPPSDK
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+#else
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+#endif
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+
+namespace CanvasLayout.Sample.SampleOne
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ [ToolkitSampleBoolOption("IsTextVisible", "IsVisible", true)]
+
+ [ToolkitSampleMultiChoiceOption("TextForeground", label: "Teal", value: "#0ddc8c", title: "Text foreground")]
+ [ToolkitSampleMultiChoiceOption("TextForeground", label: "Sand", value: "#e7a676")]
+ [ToolkitSampleMultiChoiceOption("TextForeground", label: "Dull green", value: "#5d7577")]
+
+ [ToolkitSampleMultiChoiceOption("TextSize", label: "Small", value: "12", title: "Text size")]
+ [ToolkitSampleMultiChoiceOption("TextSize", label: "Normal", value: "16")]
+ [ToolkitSampleMultiChoiceOption("TextSize", label: "Big", value: "32")]
+
+ [ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Segoe UI", value: "Segoe UI")]
+ [ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Arial", value: "Arial")]
+ [ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Consolas", value: "Consolas")]
+
+ [ToolkitSample(id: nameof(SamplePage), "Canvas Layout", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "A canvas-like VirtualizingPanel for use in an ItemsControl")]
+ public sealed partial class SamplePage : Page
+ {
+ public SamplePage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleTwo/SamplePage2.xaml.cs b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleTwo/SamplePage2.xaml.cs
index c6ff1026d..b84d8295a 100644
--- a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleTwo/SamplePage2.xaml.cs
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleTwo/SamplePage2.xaml.cs
@@ -13,7 +13,7 @@
namespace CanvasLayout.Sample.SampleTwo
{
- [ToolkitSample(id: nameof(SamplePage2), "Example sample", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "An empty sample used to demonstrate the sample system.")]
+ [ToolkitSample(id: nameof(SamplePage2), "Custom options", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "An empty sample used to demonstrate the sample system.")]
public sealed partial class SamplePage2 : UserControl
{
public SamplePage2()
diff --git a/Labs/CanvasLayout/src/CanvasLayout.cs b/Labs/CanvasLayout/src/CanvasLayout.cs
index 8f3c2edba..f4017dcde 100644
--- a/Labs/CanvasLayout/src/CanvasLayout.cs
+++ b/Labs/CanvasLayout/src/CanvasLayout.cs
@@ -2,11 +2,121 @@
// 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 Microsoft.UI.Xaml.Controls;
+using Windows.Foundation;
namespace CommunityToolkit.Labs.Uwp
{
public class CanvasLayout : VirtualizingLayout
{
+ #region Setup / teardown
+ protected override void InitializeForContextCore(VirtualizingLayoutContext context)
+ {
+ base.InitializeForContextCore(context);
+
+ if (!(context.LayoutState is CanvasLayoutState state))
+ {
+ // Store any state we might need since (in theory) the layout could be in use by multiple
+ // elements simultaneously
+ context.LayoutState = new CanvasLayoutState();
+ }
+ }
+
+ protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
+ {
+ base.UninitializeForContextCore(context);
+
+ // clear any state
+ context.LayoutState = null;
+ }
+
+ #endregion
+
+ #region Layout
+
+ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
+ {
+ int maxWidth = 0;
+ int maxHeight = 0;
+
+ // Get underlying data about positioning of items and determine if in viewport.
+ for (int i = 0; i < context.ItemCount; i++)
+ {
+ if (context.GetItemAt(i) is CanvasLayoutItem item)
+ {
+ // See if this item pushes our maximum boundary
+ maxWidth = Math.Max(item.Left + item.Width, maxWidth);
+ maxHeight = Math.Max(item.Top + item.Height, maxHeight);
+
+ // Calculate if this item is in our current viewport
+ Rect rect = new(item.Left, item.Top, item.Width, item.Height);
+ rect.Intersect(context.RealizationRect);
+
+ // TODO: If the item is currently in view and will be in view, we should recycle the element's container
+ item.IsInView = rect.Width > 0 || rect.Height > 0;
+
+ // TODO: If item is in view, we should call GetElementOrCreateAt to realize container here. (And call it's measure method.)
+ }
+ }
+
+ return new Size(maxWidth, maxHeight);
+ }
+
+ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)
+ {
+ for (int i = 0; i < context.ItemCount; i++)
+ {
+ if (context.GetItemAt(i) is CanvasLayoutItem item && item.IsInView)
+ {
+ var container = context.GetOrCreateElementAt(i);
+ // Is it better to have cached this from above?
+ container.Arrange(new Rect(item.Left, item.Top, item.Width, item.Height));
+ }
+ }
+
+ return finalSize;
+ }
+
+ #endregion
+ }
+
+ internal class CanvasLayoutState
+ {
+ public int FirstRealizedIndex { get; set; }
+
+ ///
+ /// List of layout bounds for items starting with the
+ /// FirstRealizedIndex.
+ ///
+ public List LayoutRects
+ {
+ get
+ {
+ if (_layoutRects == null)
+ {
+ _layoutRects = new List();
+ }
+
+ return _layoutRects;
+ }
+ }
+
+ private List _layoutRects;
+ }
+
+ // TODO: Make DP? Can we do this with property mapping instead?
+ public class CanvasLayoutItem
+ {
+ public int Left { get; set; }
+
+ public int Top { get; set; }
+
+ public int Width { get; set; }
+
+ public int Height { get; set; }
+
+ public bool IsInView { get; internal set; }
}
}
From 018c73030319eb33c26f53fa2118cc30dd68eb37 Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 9 Dec 2021 12:45:52 -0800
Subject: [PATCH 2/6] Add Uno Guard
---
.../samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
index 97bd1e84b..e9f9f25e5 100644
--- a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
@@ -6,6 +6,7 @@
xmlns:local="using:CanvasLayout.Sample"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:labs="using:CommunityToolkit.Labs.Uwp"
+ xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
@@ -29,7 +30,7 @@
FontSize="24"
FontWeight="Bold"
HorizontalAlignment="Center"
- TextLineBounds="Tight"
+ win:TextLineBounds="Tight"
VerticalAlignment="Center"/>
From 4f28464e463da385bb5067555ae35dda65c9eb81 Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 9 Dec 2021 13:02:25 -0800
Subject: [PATCH 3/6] Add container realization to CanvasLayout
Still has issue in UWP about scrolling getting stuck... (had tested seemed ok in WASM)
---
Labs/CanvasLayout/src/CanvasLayout.cs | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/Labs/CanvasLayout/src/CanvasLayout.cs b/Labs/CanvasLayout/src/CanvasLayout.cs
index f4017dcde..c0b9f221d 100644
--- a/Labs/CanvasLayout/src/CanvasLayout.cs
+++ b/Labs/CanvasLayout/src/CanvasLayout.cs
@@ -54,10 +54,24 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size
Rect rect = new(item.Left, item.Top, item.Width, item.Height);
rect.Intersect(context.RealizationRect);
- // TODO: If the item is currently in view and will be in view, we should recycle the element's container
+ // Check if we're in view now so we can compare to if we were last time.
+ bool nowInView = rect.Width > 0 || rect.Height > 0;
+
+ // If it wasn't visible and now is, realize the container
+ if (nowInView && !item.IsInView)
+ {
+ var element = context.GetOrCreateElementAt(i);
+ element.Measure(new Size(item.Width, item.Height));
+ }
+ // If it was visible, but now isn't recycle the container
+ else if (!nowInView && item.IsInView)
+ {
+ var element = context.GetOrCreateElementAt(i);
+ context.RecycleElement(element);
+ }
+
+ // Update our current visibility
item.IsInView = rect.Width > 0 || rect.Height > 0;
-
- // TODO: If item is in view, we should call GetElementOrCreateAt to realize container here. (And call it's measure method.)
}
}
From 0187f676d4027fa0b9efc5dad31aecc42e2f5a9e Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Wed, 12 Jan 2022 15:44:04 -0800
Subject: [PATCH 4/6] Add project references?
---
.../samples/CanvasLayout.Wasm/CanvasLayout.Wasm.csproj | 1 +
1 file changed, 1 insertion(+)
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Wasm/CanvasLayout.Wasm.csproj b/Labs/CanvasLayout/samples/CanvasLayout.Wasm/CanvasLayout.Wasm.csproj
index 9613ce94f..3588925e7 100644
--- a/Labs/CanvasLayout/samples/CanvasLayout.Wasm/CanvasLayout.Wasm.csproj
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Wasm/CanvasLayout.Wasm.csproj
@@ -7,6 +7,7 @@
+
\ No newline at end of file
From f914d3b81fb30e952bbda7ed9dba2be000d86689 Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Tue, 8 Mar 2022 16:59:28 -0800
Subject: [PATCH 5/6] Fix messed up rebase
---
.../SampleThree/SamplePage3.xaml | 5 ++-
.../SampleThree/SamplePage3.xaml.cs | 41 ++++++++++---------
2 files changed, 25 insertions(+), 21 deletions(-)
diff --git a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
index e9f9f25e5..19dc5ee21 100644
--- a/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
+++ b/Labs/CanvasLayout/samples/CanvasLayout.Sample/SampleThree/SamplePage3.xaml
@@ -1,9 +1,10 @@
/// An empty page that can be used on its own or navigated to within a Frame.
///
- [ToolkitSampleBoolOption("IsTextVisible", "IsVisible", true)]
-
- [ToolkitSampleMultiChoiceOption("TextForeground", label: "Teal", value: "#0ddc8c", title: "Text foreground")]
- [ToolkitSampleMultiChoiceOption("TextForeground", label: "Sand", value: "#e7a676")]
- [ToolkitSampleMultiChoiceOption("TextForeground", label: "Dull green", value: "#5d7577")]
-
- [ToolkitSampleMultiChoiceOption("TextSize", label: "Small", value: "12", title: "Text size")]
- [ToolkitSampleMultiChoiceOption("TextSize", label: "Normal", value: "16")]
- [ToolkitSampleMultiChoiceOption("TextSize", label: "Big", value: "32")]
-
- [ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Segoe UI", value: "Segoe UI")]
- [ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Arial", value: "Arial")]
- [ToolkitSampleMultiChoiceOption("TextFontFamily", label: "Consolas", value: "Consolas")]
-
- [ToolkitSample(id: nameof(SamplePage), "Canvas Layout", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "A canvas-like VirtualizingPanel for use in an ItemsControl")]
- public sealed partial class SamplePage : Page
+ [ToolkitSample(id: nameof(SamplePage3), "Canvas Layout", ToolkitSampleCategory.Controls, ToolkitSampleSubcategory.Layout, description: "A canvas-like VirtualizingLayout for use in an ItemsRepeater")]
+ public sealed partial class SamplePage3 : Page
{
- public SamplePage()
+ public ObservableCollection Items = new()
+ {
+ new() { Left = 100, Top = 50, Width = 100, Height = 100, Text = "Item 1" },
+ new() { Left = 400, Top = 250, Width = 200, Height = 200, Text = "Item 2" },
+ new() { Left = 200, Top = 500, Width = 100, Height = 100, Text = "Item 3" },
+ new() { Left = 1200, Top = 2500, Width = 100, Height = 100, Text = "Item 4" },
+ new() { Left = 2200, Top = 1500, Width = 100, Height = 100, Text = "Item 5" },
+ new() { Left = 1200, Top = 3500, Width = 100, Height = 100, Text = "Item 6" },
+ };
+
+ public SamplePage3()
{
this.InitializeComponent();
}
}
+
+ public class CanvasItem : CanvasLayoutItem
+ {
+ public string Text { get; set; }
+ }
}
From 8efb390c95d7f56350b67697813d208add0019a5 Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Wed, 9 Mar 2022 10:55:02 -0800
Subject: [PATCH 6/6] Add missing project reference
---
CommunityToolkit.Labs.Uwp/CommunityToolkit.Labs.Uwp.csproj | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CommunityToolkit.Labs.Uwp/CommunityToolkit.Labs.Uwp.csproj b/CommunityToolkit.Labs.Uwp/CommunityToolkit.Labs.Uwp.csproj
index fc4e9fff7..344a4e4eb 100644
--- a/CommunityToolkit.Labs.Uwp/CommunityToolkit.Labs.Uwp.csproj
+++ b/CommunityToolkit.Labs.Uwp/CommunityToolkit.Labs.Uwp.csproj
@@ -25,6 +25,10 @@
{a14189c0-39a8-4fbe-bf86-a78a94654c48}
CanvasLayout.Sample
+
+ {fe19fff0-6ab6-4fc7-bfdf-b6499153dcd5}
+ CommunityToolkit.Labs.Uwp.UI.CanvasLayout
+
\ No newline at end of file