From 0a25496d9dd59a86c7aa62082a68eee55696fa91 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 22 Dec 2023 10:03:21 -0500 Subject: [PATCH 01/14] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a73a38f..1408c92 100644 --- a/.gitignore +++ b/.gitignore @@ -349,3 +349,5 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ .DS_Store + +.idea/ \ No newline at end of file From 8a9b058e0ae206ced959353daae3596481dd8ab3 Mon Sep 17 00:00:00 2001 From: PavloLukianets <109070173+PavloLukianets@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:40:41 +0200 Subject: [PATCH 02/14] Add plain net8 target --- .gitignore | 3 +++ VirtualListView/VirtualListView.csproj | 2 +- VirtualListView/VirtualListViewHandler.cs | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a73a38f..e98f007 100644 --- a/.gitignore +++ b/.gitignore @@ -349,3 +349,6 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ .DS_Store + +# Rider +.idea \ No newline at end of file diff --git a/VirtualListView/VirtualListView.csproj b/VirtualListView/VirtualListView.csproj index 04f3bce..258a9a0 100644 --- a/VirtualListView/VirtualListView.csproj +++ b/VirtualListView/VirtualListView.csproj @@ -1,6 +1,6 @@ - net8.0-android;net8.0-ios;net8.0-maccatalyst + net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst $(TargetFrameworks);net8.0-windows10.0.19041 true diff --git a/VirtualListView/VirtualListViewHandler.cs b/VirtualListView/VirtualListViewHandler.cs index 2e73958..f07be86 100644 --- a/VirtualListView/VirtualListViewHandler.cs +++ b/VirtualListView/VirtualListViewHandler.cs @@ -4,6 +4,7 @@ namespace Microsoft.Maui; public partial class VirtualListViewHandler { + #if ANDROID || IOS || MACCATALYST || WINDOWS public static new IPropertyMapper ViewMapper = new PropertyMapper(Handlers.ViewHandler.ViewMapper) { [nameof(IVirtualListView.Adapter)] = MapAdapter, @@ -135,5 +136,6 @@ public static void MapIsFooterVisible(VirtualListViewHandler handler, IVirtualLi { handler?.InvalidateData(); } +#endif } \ No newline at end of file From dcbc64f63897a077901f813bd7125ff0d4c7eaec Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 24 May 2024 09:47:39 -0400 Subject: [PATCH 03/14] Fix merge --- VirtualListView/VirtualListViewHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VirtualListView/VirtualListViewHandler.cs b/VirtualListView/VirtualListViewHandler.cs index 37ce16a..6ea1977 100644 --- a/VirtualListView/VirtualListViewHandler.cs +++ b/VirtualListView/VirtualListViewHandler.cs @@ -148,5 +148,5 @@ public static void MapHorizontalScrollbarVisibility(VirtualListViewHandler handl { handler?.UpdateHorizontalScrollbarVisibility(virtualListView.HorizontalScrollbarVisibility); } - +#endif } \ No newline at end of file From 8d504c452bea77b546a5437b3858712663acba9e Mon Sep 17 00:00:00 2001 From: Daniel Hufnagl Date: Mon, 27 May 2024 17:19:44 +0200 Subject: [PATCH 04/14] remove null checks --- VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs index 2d2dd99..24afc87 100644 --- a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs +++ b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs @@ -72,7 +72,7 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS info.IsSelected = Handler?.IsItemSelected(info.SectionIndex, info.ItemIndex) ?? false; } - if (cell.NeedsView && info is not null && data is not null) + if (cell.NeedsView) { var view = Handler?.PositionalViewSelector?.ViewSelector?.CreateView(info, data); if (view is not null) From 90af7a5307eca752ed6d3d655b68bb1d95aa92fd Mon Sep 17 00:00:00 2001 From: Daniel Hufnagl Date: Tue, 28 May 2024 09:40:22 +0200 Subject: [PATCH 05/14] fix null exception in totalcount --- VirtualListView/PositionalViewSelector.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/VirtualListView/PositionalViewSelector.cs b/VirtualListView/PositionalViewSelector.cs index 8b93b4a..8381fb1 100644 --- a/VirtualListView/PositionalViewSelector.cs +++ b/VirtualListView/PositionalViewSelector.cs @@ -30,6 +30,10 @@ int GetTotalCount() var hasAtLeastOneItem = false; var numberOfSections = Adapter.GetNumberOfSections(); + if (Adapter is null) + { + return 0; + } if (HasGlobalHeader && numberOfSections > 0) { From 12b0880bfdf60da880c873291c490b73daf8bcda Mon Sep 17 00:00:00 2001 From: Daniel Hufnagl Date: Fri, 31 May 2024 15:49:28 +0200 Subject: [PATCH 06/14] Fix putting null check earlier --- VirtualListView/PositionalViewSelector.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/VirtualListView/PositionalViewSelector.cs b/VirtualListView/PositionalViewSelector.cs index 8381fb1..198bb9c 100644 --- a/VirtualListView/PositionalViewSelector.cs +++ b/VirtualListView/PositionalViewSelector.cs @@ -26,14 +26,15 @@ public int TotalCount int GetTotalCount() { - var sum = 0; - - var hasAtLeastOneItem = false; - var numberOfSections = Adapter.GetNumberOfSections(); if (Adapter is null) { return 0; } + + var sum = 0; + + var hasAtLeastOneItem = false; + var numberOfSections = Adapter.GetNumberOfSections(); if (HasGlobalHeader && numberOfSections > 0) { From 137e2af5f8ff05dfd9a2a4078e5e40e12ed87096 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 13:21:27 -0400 Subject: [PATCH 07/14] Still check info, but not data Headers and footers can have null/no data but they should still have info --- .../Apple/CvDataSource.ios.maccatalyst.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs index 24afc87..015893b 100644 --- a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs +++ b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs @@ -70,23 +70,19 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS info.IsSelected = false; else info.IsSelected = Handler?.IsItemSelected(info.SectionIndex, info.ItemIndex) ?? false; - } - - if (cell.NeedsView) - { - var view = Handler?.PositionalViewSelector?.ViewSelector?.CreateView(info, data); - if (view is not null) - cell.SetupView(view); - } + + if (cell.NeedsView) + { + var view = Handler?.PositionalViewSelector?.ViewSelector?.CreateView(info, data); + if (view is not null) + cell.SetupView(view); + } - if (info is not null) - { cell.UpdatePosition(info); - if (data is not null && (cell.VirtualView?.TryGetTarget(out var cellVirtualView) ?? false)) + if (cell.VirtualView?.TryGetTarget(out var cellVirtualView) ?? false) { Handler?.PositionalViewSelector?.ViewSelector?.RecycleView(info, data, cellVirtualView); - Handler?.VirtualView?.ViewSelector?.ViewAttached(info, cellVirtualView); } } From 685b7729ea6a5eac8f57034aa9549d520c1e0ee3 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 14:01:20 -0400 Subject: [PATCH 08/14] Cleanup --- VirtualListView/Apple/CvCell.ios.maccatalyst.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/VirtualListView/Apple/CvCell.ios.maccatalyst.cs b/VirtualListView/Apple/CvCell.ios.maccatalyst.cs index 517b642..ce8b4fa 100644 --- a/VirtualListView/Apple/CvCell.ios.maccatalyst.cs +++ b/VirtualListView/Apple/CvCell.ios.maccatalyst.cs @@ -1,8 +1,6 @@ using CoreGraphics; using Foundation; using Microsoft.Maui.Platform; -using Microsoft.VisualBasic; -using System.Diagnostics.CodeAnalysis; using UIKit; namespace Microsoft.Maui; From d2485b5f9dd0f681ba34da87a8fcccbeba19d03f Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 14:03:04 -0400 Subject: [PATCH 09/14] Add more nullability The data can be null for global header / footer items, so adding some nullability to try and validate if anything else was expecting this. --- VirtualListView/Controls/VirtualListView.cs | 51 ++++++++++--------- .../VirtualListViewItemTemplateSelector.cs | 8 +-- VirtualListView/IVirtualListViewSelector.cs | 9 ++-- VirtualListView/VirtualListViewExtensions.cs | 5 +- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/VirtualListView/Controls/VirtualListView.cs b/VirtualListView/Controls/VirtualListView.cs index a6d0d73..35e917c 100644 --- a/VirtualListView/Controls/VirtualListView.cs +++ b/VirtualListView/Controls/VirtualListView.cs @@ -1,4 +1,5 @@ -using System.Windows.Input; +#nullable enable +using System.Windows.Input; using Microsoft.Maui.Adapters; namespace Microsoft.Maui.Controls; @@ -21,7 +22,7 @@ public IVirtualListViewAdapter Adapter BindableProperty.Create(nameof(Adapter), typeof(IVirtualListViewAdapter), typeof(VirtualListView), default); - public IView GlobalHeader + public IView? GlobalHeader { get => (IView)GetValue(GlobalHeaderProperty); set => SetValue(GlobalHeaderProperty, value); @@ -39,7 +40,7 @@ public bool IsHeaderVisible public static readonly BindableProperty IsHeaderVisibleProperty = BindableProperty.Create(nameof(IsHeaderVisible), typeof(bool), typeof(VirtualListView), true); - public IView GlobalFooter + public IView? GlobalFooter { get => (IView)GetValue(GlobalFooterProperty); set => SetValue(GlobalFooterProperty, value); @@ -59,7 +60,7 @@ public bool IsFooterVisible BindableProperty.Create(nameof(IsFooterVisible), typeof(bool), typeof(VirtualListView), true); - public DataTemplate ItemTemplate + public DataTemplate? ItemTemplate { get => (DataTemplate)GetValue(ItemTemplateProperty); set => SetValue(ItemTemplateProperty, value); @@ -68,7 +69,7 @@ public DataTemplate ItemTemplate public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(VirtualListView), default); - public VirtualListViewItemTemplateSelector ItemTemplateSelector + public VirtualListViewItemTemplateSelector? ItemTemplateSelector { get => (VirtualListViewItemTemplateSelector)GetValue(ItemTemplateSelectorProperty); set => SetValue(ItemTemplateSelectorProperty, value); @@ -97,7 +98,7 @@ public ScrollBarVisibility HorizontalScrollbarVisibility public static readonly BindableProperty HorizontalScrollbarVisibilityProperty = BindableProperty.Create(nameof(HorizontalScrollbarVisibility), typeof(ScrollBarVisibility), typeof(VirtualListView), ScrollBarVisibility.Default); - public DataTemplate SectionHeaderTemplate + public DataTemplate? SectionHeaderTemplate { get => (DataTemplate)GetValue(SectionHeaderTemplateProperty); set => SetValue(SectionHeaderTemplateProperty, value); @@ -106,7 +107,7 @@ public DataTemplate SectionHeaderTemplate public static readonly BindableProperty SectionHeaderTemplateProperty = BindableProperty.Create(nameof(SectionHeaderTemplate), typeof(DataTemplate), typeof(VirtualListView), default); - public VirtualListViewSectionTemplateSelector SectionHeaderTemplateSelector + public VirtualListViewSectionTemplateSelector? SectionHeaderTemplateSelector { get => (VirtualListViewSectionTemplateSelector)GetValue(SectionHeaderTemplateSelectorProperty); set => SetValue(SectionHeaderTemplateSelectorProperty, value); @@ -117,7 +118,7 @@ public VirtualListViewSectionTemplateSelector SectionHeaderTemplateSelector - public DataTemplate SectionFooterTemplate + public DataTemplate? SectionFooterTemplate { get => (DataTemplate)GetValue(SectionFooterTemplateProperty); set => SetValue(SectionFooterTemplateProperty, value); @@ -126,7 +127,7 @@ public DataTemplate SectionFooterTemplate public static readonly BindableProperty SectionFooterTemplateProperty = BindableProperty.Create(nameof(SectionFooterTemplate), typeof(DataTemplate), typeof(VirtualListView), default); - public VirtualListViewSectionTemplateSelector SectionFooterTemplateSelector + public VirtualListViewSectionTemplateSelector? SectionFooterTemplateSelector { get => (VirtualListViewSectionTemplateSelector)GetValue(SectionFooterTemplateSelectorProperty); set => SetValue(SectionFooterTemplateSelectorProperty, value); @@ -159,7 +160,7 @@ void IVirtualListView.Refresh(Action completionCallback) OnRefresh?.Invoke(this, new RefreshEventArgs(completionCallback)); } - public ICommand RefreshCommand + public ICommand? RefreshCommand { get => (ICommand)GetValue(RefreshCommandProperty); set => SetValue(RefreshCommandProperty, value); @@ -168,9 +169,9 @@ public ICommand RefreshCommand public static readonly BindableProperty RefreshCommandProperty = BindableProperty.Create(nameof(RefreshCommand), typeof(ICommand), typeof(VirtualListView), default); - public Color RefreshAccentColor + public Color? RefreshAccentColor { - get => (Color)GetValue(RefreshAccentColorProperty); + get => (Color?)GetValue(RefreshAccentColorProperty); set => SetValue(RefreshAccentColorProperty, value); } @@ -196,14 +197,14 @@ public ListOrientation Orientation BindableProperty.Create(nameof(Orientation), typeof(ListOrientation), typeof(VirtualListView), ListOrientation.Vertical); - public View EmptyView + public IView? EmptyView { - get => (View)GetValue(EmptyViewProperty); + get => (IView)GetValue(EmptyViewProperty); set => SetValue(EmptyViewProperty, value); } public static readonly BindableProperty EmptyViewProperty = - BindableProperty.Create(nameof(EmptyView), typeof(View), typeof(VirtualListView), null, + BindableProperty.Create(nameof(EmptyView), typeof(IView), typeof(VirtualListView), null, propertyChanged: (bobj, oldValue, newValue) => { if (bobj is VirtualListView virtualListView) @@ -216,13 +217,13 @@ public View EmptyView } }); - IView IVirtualListView.EmptyView => EmptyView; + IView? IVirtualListView.EmptyView => EmptyView; public IVirtualListViewSelector ViewSelector => this; - public IView Header => GlobalHeader; - public IView Footer => GlobalFooter; + public IView? Header => GlobalHeader; + public IView? Footer => GlobalFooter; public event EventHandler OnScrolled; @@ -239,7 +240,7 @@ public void Scrolled(double x, double y) public static readonly BindableProperty ScrolledCommandProperty = BindableProperty.Create(nameof(ScrolledCommand), typeof(ICommand), typeof(VirtualListView), default); - public ICommand ScrolledCommand + public ICommand? ScrolledCommand { get => (ICommand)GetValue(ScrolledCommandProperty); set => SetValue(ScrolledCommandProperty, value); @@ -256,7 +257,7 @@ public ICommand ScrolledCommand vlv.RaiseSelectedItemsChanged(oldSelection.ToArray(), newSelection.ToArray()); } }); - public IList SelectedItems + public IList? SelectedItems { get => (IList)GetValue(SelectedItemsProperty); set => SetValue(SelectedItemsProperty, value ?? Array.Empty()); @@ -314,9 +315,9 @@ public void SelectItem(ItemPosition itemPosition) else if (SelectionMode == Maui.SelectionMode.Multiple) { var current = SelectedItems; - if (!current.Contains(itemPosition)) + if (current is null || !current.Contains(itemPosition)) { - SelectedItems = current.Append(itemPosition).ToArray(); + SelectedItems = (current ?? []).Append(itemPosition).ToArray(); } } } @@ -338,7 +339,7 @@ public bool SectionHasHeader(int sectionIndex) public bool SectionHasFooter(int sectionIndex) => SectionFooterTemplateSelector != null || SectionFooterTemplate != null; - public IView CreateView(PositionInfo position, object data) + public IView? CreateView(PositionInfo position, object? data) => position.Kind switch { PositionKind.Item => @@ -357,7 +358,7 @@ public IView CreateView(PositionInfo position, object data) _ => default }; - public void RecycleView(PositionInfo position, object data, IView view) + public void RecycleView(PositionInfo position, object? data, IView view) { if (view is View controlsView) { @@ -370,7 +371,7 @@ public void RecycleView(PositionInfo position, object data, IView view) } } - public string GetReuseId(PositionInfo position, object data) + public string GetReuseId(PositionInfo position, object? data) => position.Kind switch { PositionKind.Item => diff --git a/VirtualListView/Controls/VirtualListViewItemTemplateSelector.cs b/VirtualListView/Controls/VirtualListViewItemTemplateSelector.cs index f2c93e6..72e4780 100644 --- a/VirtualListView/Controls/VirtualListViewItemTemplateSelector.cs +++ b/VirtualListView/Controls/VirtualListViewItemTemplateSelector.cs @@ -1,11 +1,13 @@ -namespace Microsoft.Maui.Controls; +#nullable enable + +namespace Microsoft.Maui.Controls; public abstract class VirtualListViewItemTemplateSelector { - public abstract DataTemplate SelectTemplate(object item, int sectionIndex, int itemIndex); + public abstract DataTemplate SelectTemplate(object? item, int sectionIndex, int itemIndex); } public abstract class VirtualListViewSectionTemplateSelector { - public abstract DataTemplate SelectTemplate(object section, int sectionIndex); + public abstract DataTemplate SelectTemplate(object? section, int sectionIndex); } diff --git a/VirtualListView/IVirtualListViewSelector.cs b/VirtualListView/IVirtualListViewSelector.cs index e848eee..824eba6 100644 --- a/VirtualListView/IVirtualListViewSelector.cs +++ b/VirtualListView/IVirtualListViewSelector.cs @@ -1,13 +1,14 @@ -namespace Microsoft.Maui; +#nullable enable +namespace Microsoft.Maui; public interface IVirtualListViewSelector { bool SectionHasHeader(int sectionIndex); bool SectionHasFooter(int sectionIndex); - IView CreateView(PositionInfo position, object data); - void RecycleView(PositionInfo position, object data, IView view); - string GetReuseId(PositionInfo position, object data); + IView? CreateView(PositionInfo position, object? data); + void RecycleView(PositionInfo position, object? data, IView view); + string GetReuseId(PositionInfo position, object? data); void ViewDetached(PositionInfo position, IView view) { } diff --git a/VirtualListView/VirtualListViewExtensions.cs b/VirtualListView/VirtualListViewExtensions.cs index 4142306..951e1e6 100644 --- a/VirtualListView/VirtualListViewExtensions.cs +++ b/VirtualListView/VirtualListViewExtensions.cs @@ -1,10 +1,11 @@ -using Microsoft.Maui.Adapters; +#nullable enable +using Microsoft.Maui.Adapters; namespace Microsoft.Maui; internal static class VirtualListViewExtensions { - public static object DataFor(this IVirtualListViewAdapter vlva, PositionKind kind, int sectionIndex, int itemIndex) + public static object? DataFor(this IVirtualListViewAdapter vlva, PositionKind kind, int sectionIndex, int itemIndex) => kind switch { PositionKind.Item => vlva.GetItem(sectionIndex, itemIndex), From 39c17138138ece2e008011d5d998303f06b5e1d9 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 14:07:30 -0400 Subject: [PATCH 10/14] Delete old comet extension tasks --- .vscode/launch.json | 14 -------------- .vscode/tasks.json | 12 ------------ 2 files changed, 26 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 659da7a..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Debug", - "type": "comet", - "request": "launch", - "preLaunchTask": "comet: Build" - } - ] -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index acb55cc..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "comet", - "problemMatcher": [ - "$msCompile" - ], - "label": "comet: Build" - } - ] -} \ No newline at end of file From 65366cc8c42879d26791a663b772289f9794da04 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 14:31:29 -0400 Subject: [PATCH 11/14] Start adding FindVisiblePositions --- .../VirtualListViewHandler.ios.maccatalyst.cs | 13 +++++++++++++ VirtualListView/IVirtualListView.cs | 2 ++ .../Android/VirtualListViewHandler.android.cs | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs b/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs index 84e4f2c..b86f113 100644 --- a/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs +++ b/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs @@ -255,4 +255,17 @@ public void InvalidateData() }); } + + public IReadOnlyList FindVisiblePositions() + { + var positions = new List(); + + foreach (var cell in PlatformView.VisibleCells) + { + if (cell is CvCell cvCell) + positions.Add(cvCell.PositionInfo); + } + + return positions; + } } diff --git a/VirtualListView/IVirtualListView.cs b/VirtualListView/IVirtualListView.cs index eea3308..8d739e7 100644 --- a/VirtualListView/IVirtualListView.cs +++ b/VirtualListView/IVirtualListView.cs @@ -49,6 +49,8 @@ public interface IVirtualListView : IView void ClearSelectedItems(); void ScrollToItem(ItemPosition path, bool animated); + + IReadOnlyList FindVisiblePositions(); } public enum ListOrientation diff --git a/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs b/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs index 4f4e8cc..0fd9646 100644 --- a/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs +++ b/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs @@ -195,4 +195,19 @@ void UpdateHorizontalScrollbarVisibility(ScrollBarVisibility scrollBarVisibility recyclerView.HorizontalScrollBarEnabled = newHorizontalScrollVisiblility == ScrollBarVisibility.Always; } + + public IReadOnlyList FindVisiblePositions() + { + var positions = new List(); + + var firstVisibleItemPosition = layoutManager.FindFirstVisibleItemPosition(); + var lastVisibleItemPosition = layoutManager.FindLastVisibleItemPosition(); + + for (var p = firstVisibleItemPosition; p <= lastVisibleItemPosition; p++) + { + positions.Add(PositionalViewSelector.GetInfo(p)); + } + + return positions; + } } \ No newline at end of file From 3bc5a85062973c9cb5d2d61e89fd76a066b67813 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 15:19:54 -0400 Subject: [PATCH 12/14] Finish implementation for finding visible positons --- VirtualListView/Controls/VirtualListView.cs | 8 +++++ VirtualListView/IVirtualListViewHandler.cs | 8 +++++ .../Windows/VirtualListViewHandler.windows.cs | 35 +++++++++++++++++++ VirtualListView/VirtualListViewHandler.cs | 2 +- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 VirtualListView/IVirtualListViewHandler.cs diff --git a/VirtualListView/Controls/VirtualListView.cs b/VirtualListView/Controls/VirtualListView.cs index 35e917c..cb890bf 100644 --- a/VirtualListView/Controls/VirtualListView.cs +++ b/VirtualListView/Controls/VirtualListView.cs @@ -402,4 +402,12 @@ public void ViewAttached(PositionInfo position, IView view) void RaiseSelectedItemsChanged(ItemPosition[] previousSelection, ItemPosition[] newSelection) => this.OnSelectedItemsChanged?.Invoke(this, new SelectedItemsChangedEventArgs(previousSelection, newSelection)); + public IReadOnlyList FindVisiblePositions() + { +#if ANDROID || IOS || MACCATALYST || WINDOWS + if (Handler is IVirtualListViewHandler handler) + return handler.FindVisiblePositions(); +#endif + return []; + } } diff --git a/VirtualListView/IVirtualListViewHandler.cs b/VirtualListView/IVirtualListViewHandler.cs new file mode 100644 index 0000000..a67e9b0 --- /dev/null +++ b/VirtualListView/IVirtualListViewHandler.cs @@ -0,0 +1,8 @@ +namespace Microsoft.Maui; + +public interface IVirtualListViewHandler +{ +#if ANDROID || IOS || MACCATALYST || WINDOWS + IReadOnlyList FindVisiblePositions(); +#endif +} diff --git a/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs b/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs index 9c0d766..86e1be6 100644 --- a/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs +++ b/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs @@ -5,8 +5,10 @@ using WVisibility = Microsoft.UI.Xaml.Visibility; using WFrameworkElement = Microsoft.UI.Xaml.FrameworkElement; using WScrollBarVisibility = Microsoft.UI.Xaml.Controls.ScrollBarVisibility; +using WRect = Windows.Foundation.Rect; using Microsoft.Maui.Platform; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; namespace Microsoft.Maui; @@ -187,4 +189,37 @@ void UpdateHorizontalScrollbarVisibility(ScrollBarVisibility scrollBarVisibility _ => WScrollBarVisibility.Auto }; } + + public IReadOnlyList FindVisiblePositions() + { + var positions = new List(); + + // This gets us all the direct children of our repeater which should be IrElementContainers + // The items will not all be visible, since virtualizing causes items to get setup out of view + // before being displayed. We need to check if they are actually visible. + var childrenCount = VisualTreeHelper.GetChildrenCount(itemsRepeater); + + for (int i = 0; i < childrenCount; i++) + { + // See if the child is an IrElementContainer + if (VisualTreeHelper.GetChild(itemsRepeater, i) is IrElementContainer irElementContainer) + { + // Is the item actually visible? + if (VisibilityHitTest(irElementContainer, itemsRepeater)) + positions.Add(irElementContainer.PositionInfo); + } + } + + return positions; + } + + static bool VisibilityHitTest(IrElementContainer element, FrameworkElement container) + { + var bounds = element.TransformToVisual(container).TransformBounds(new WRect(0.0, 0.0, element.ActualWidth, element.ActualHeight)); + + return bounds.Left < container.ActualWidth + && bounds.Top < container.ActualHeight + && bounds.Right > 0 + && bounds.Bottom > 0; + } } diff --git a/VirtualListView/VirtualListViewHandler.cs b/VirtualListView/VirtualListViewHandler.cs index 6ea1977..79cf0bd 100644 --- a/VirtualListView/VirtualListViewHandler.cs +++ b/VirtualListView/VirtualListViewHandler.cs @@ -2,7 +2,7 @@ namespace Microsoft.Maui; -public partial class VirtualListViewHandler +public partial class VirtualListViewHandler : IVirtualListViewHandler { #if ANDROID || IOS || MACCATALYST || WINDOWS public static new IPropertyMapper ViewMapper = new PropertyMapper(Handlers.ViewHandler.ViewMapper) From 4eb498fb0411097414b36a72775d25b7c0f7262d Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 16:05:45 -0400 Subject: [PATCH 13/14] Fix bad merge reverting a data check for header/footer --- .../Apple/CvDataSource.ios.maccatalyst.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs index 015893b..fadafa0 100644 --- a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs +++ b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs @@ -33,21 +33,18 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS if (info is not null) { data = Handler?.PositionalViewSelector?.Adapter?.DataFor(info.Kind, info.SectionIndex, info.ItemIndex); + + var reuseId = Handler?.PositionalViewSelector?.ViewSelector?.GetReuseId(info, data); - if (data is not null) + nativeReuseId = info.Kind switch { - var reuseId = Handler?.PositionalViewSelector?.ViewSelector?.GetReuseId(info, data); - - nativeReuseId = info.Kind switch - { - PositionKind.Item => itemIdManager.GetReuseId(collectionView, reuseId), - PositionKind.SectionHeader => sectionHeaderIdManager.GetReuseId(collectionView, reuseId), - PositionKind.SectionFooter => sectionFooterIdManager.GetReuseId(collectionView, reuseId), - PositionKind.Header => globalIdManager.GetReuseId(collectionView, reuseId), - PositionKind.Footer => globalIdManager.GetReuseId(collectionView, reuseId), - _ => CvCell.ReuseIdUnknown, - }; - } + PositionKind.Item => itemIdManager.GetReuseId(collectionView, reuseId), + PositionKind.SectionHeader => sectionHeaderIdManager.GetReuseId(collectionView, reuseId), + PositionKind.SectionFooter => sectionFooterIdManager.GetReuseId(collectionView, reuseId), + PositionKind.Header => globalIdManager.GetReuseId(collectionView, reuseId), + PositionKind.Footer => globalIdManager.GetReuseId(collectionView, reuseId), + _ => CvCell.ReuseIdUnknown, + }; } var nativeCell = collectionView.DequeueReusableCell(nativeReuseId, indexPath); From cf2e554abad2ff5aa724ab1ee1422adfa959feeb Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 7 Jun 2024 16:32:48 -0400 Subject: [PATCH 14/14] Fix logic for 'at least one' item We only show global header/footer if there's at least one item to display. The header was fine already, but we weren't considering other cases where there were items found in order to decide to display the footer. So if you had no header visible, but wanted a footer, it wouldn't show up. --- VirtualListView/PositionalViewSelector.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/VirtualListView/PositionalViewSelector.cs b/VirtualListView/PositionalViewSelector.cs index 198bb9c..f2aeedf 100644 --- a/VirtualListView/PositionalViewSelector.cs +++ b/VirtualListView/PositionalViewSelector.cs @@ -33,7 +33,7 @@ int GetTotalCount() var sum = 0; - var hasAtLeastOneItem = false; + //var hasAtLeastOneItem = false; var numberOfSections = Adapter.GetNumberOfSections(); if (HasGlobalHeader && numberOfSections > 0) @@ -50,7 +50,6 @@ int GetTotalCount() // If we found one, we can stop looping // since we just care to calculate a spot // for the header cell if the adapter isn't empty - hasAtLeastOneItem = true; break; } } @@ -72,7 +71,7 @@ int GetTotalCount() // Only count footer if there is already at least one item // otherwise the adapter is empty and we shouldn't count it - if (HasGlobalFooter && hasAtLeastOneItem) + if (HasGlobalFooter && sum > 0) sum += 1; return sum;