From 89fe25015225f5b975d25f3fb36dfded62eb5b5d Mon Sep 17 00:00:00 2001 From: redth Date: Thu, 27 Jul 2023 15:27:08 -0400 Subject: [PATCH 01/14] Simplify selected items This moves to use a simpler property for SelectedItems instead of just methods, and uses the propertymapper instead to manage things between core/controls --- .../MusicLibraryPage.xaml.cs | 4 +- .../ObservableCollectionPage.xaml.cs | 19 ++- .../SectionedAdapterPage.xaml.cs | 2 +- .../Apple/CvDataSource.ios.maccatalyst.cs | 4 +- .../Apple/CvDelegate.ios.maccatalyst.cs | 4 +- .../VirtualListViewHandler.ios.maccatalyst.cs | 45 ++---- VirtualListView/Controls/VirtualListView.cs | 63 ++++++-- VirtualListView/IVirtualListView.cs | 17 +-- .../Platforms/Android/RvAdapter.cs | 6 +- .../Android/VirtualListViewHandler.android.cs | 29 +--- .../Windows/VirtualListViewHandler.windows.cs | 6 +- VirtualListView/VirtualListViewHandler.cs | 138 +++--------------- 12 files changed, 127 insertions(+), 210 deletions(-) diff --git a/Sample/VirtualListViewSample/MusicLibraryPage.xaml.cs b/Sample/VirtualListViewSample/MusicLibraryPage.xaml.cs index 59f3153..503918a 100644 --- a/Sample/VirtualListViewSample/MusicLibraryPage.xaml.cs +++ b/Sample/VirtualListViewSample/MusicLibraryPage.xaml.cs @@ -17,7 +17,9 @@ public MusicLibraryPage() { Dispatcher.Dispatch(() => { - vlv.SelectItems(new ItemPosition(0, 2), new ItemPosition(0, 4)); + vlv.SelectItem(new ItemPosition(0, 2)); + vlv.SelectItem(new ItemPosition(0, 4)); + }); }); } diff --git a/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs b/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs index e98ad45..66c9713 100644 --- a/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs +++ b/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs @@ -17,11 +17,15 @@ public ObservableCollectionPage() } vlv.Adapter = Adapter; + + //vlv.SetBinding(VirtualListView.SelectedItemsProperty, new Binding("SelectedItems", BindingMode.TwoWay, source: this)); } public ObservableCollectionAdapter Adapter { get; set; } public ObservableCollection Items = new(); + //public ItemPosition[] SelectedItems = Array.Empty(); + protected override void OnAppearing() { base.OnAppearing(); @@ -47,13 +51,16 @@ private void Button_Clicked(object sender, EventArgs e) private void vlv_SelectedItemsChanged(object sender, SelectedItemsChangedEventArgs e) { - var item = e.NewSelection?.FirstOrDefault(); - - if (item != null) + if (e.NewSelection.Any()) { - Items.RemoveAt(item.Value.ItemIndex); - } + var toDelete = e.NewSelection.First(); - (sender as IVirtualListView).ClearSelection(); + var item = Adapter.GetItem(toDelete.SectionIndex, toDelete.ItemIndex); + + vlv.ClearSelectedItems(); + + Items.Remove(item); + } + } } \ No newline at end of file diff --git a/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs b/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs index 96fd53d..01cc921 100644 --- a/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs +++ b/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs @@ -45,6 +45,6 @@ private void vlv_SelectedItemsChanged(object sender, SelectedItemsChangedEventAr Adapter.RemoveItem(item.Value.SectionIndex, item.Value.ItemIndex); } - (sender as IVirtualListView)?.ClearSelection(); + (sender as VirtualListView).SelectedItems = null; } } diff --git a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs index 42e98c8..6adadea 100644 --- a/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs +++ b/VirtualListView/Apple/CvDataSource.ios.maccatalyst.cs @@ -79,9 +79,9 @@ void TapCellHandler(CvCell cell) cell.PositionInfo.IsSelected = !cell.PositionInfo.IsSelected; if (cell.PositionInfo.IsSelected) - Handler.VirtualView?.SelectItems(p); + Handler?.VirtualView?.SelectItem(p); else - Handler.VirtualView?.DeselectItems(p); + Handler?.VirtualView?.DeselectItem(p); } public override nint GetItemsCount(UICollectionView collectionView, nint section) diff --git a/VirtualListView/Apple/CvDelegate.ios.maccatalyst.cs b/VirtualListView/Apple/CvDelegate.ios.maccatalyst.cs index c16b915..9495bdf 100644 --- a/VirtualListView/Apple/CvDelegate.ios.maccatalyst.cs +++ b/VirtualListView/Apple/CvDelegate.ios.maccatalyst.cs @@ -38,9 +38,9 @@ void HandleSelection(UICollectionView collectionView, NSIndexPath indexPath, boo selectedCell.PositionInfo.ItemIndex); if (selected) - Handler?.VirtualView?.SelectItems(itemPos); + Handler?.VirtualView?.SelectItem(itemPos); else - Handler?.VirtualView?.DeselectItems(itemPos); + Handler?.VirtualView?.DeselectItem(itemPos); } } diff --git a/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs b/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs index 8b82d37..cd62df0 100644 --- a/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs +++ b/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs @@ -63,7 +63,7 @@ protected override void ConnectHandler(UICollectionView nativeView) dataSource = new CvDataSource(this); dataSource.IsSelectedHandler = (realSection, realIndex) => - VirtualView?.IsItemSelected(realSection, realIndex) ?? false; + IsItemSelected(realSection, realIndex); cvdelegate = new CvDelegate(this, collectionView); cvdelegate.ScrollHandler = (x, y) => @@ -117,44 +117,27 @@ public static void MapSelectionMode(VirtualListViewHandler handler, IVirtualList public static void MapInvalidateData(VirtualListViewHandler handler, IVirtualListView virtualListView, object? parameter) => handler?.InvalidateData(); - public static void MapSelectItems(VirtualListViewHandler handler, IVirtualListView virtualListView, object parameter) + void PlatformUpdateItemSelection(ItemPosition itemPosition, bool selected) { - if (parameter is ItemPosition[] items) - { - UpdateSelection(handler, items, true); - } - } + var realIndex = PositionalViewSelector?.GetPosition(itemPosition.SectionIndex, itemPosition.ItemIndex) ?? -1; - public static void MapDeselectItems(VirtualListViewHandler handler, IVirtualListView virtualListView, object parameter) - { - if (parameter is ItemPosition[] items) - { - UpdateSelection(handler, items, false); - } - } + if (realIndex < 0) + return; - static void UpdateSelection(VirtualListViewHandler handler, ItemPosition[] itemPositions, bool selected) - { - foreach (var itemPosition in itemPositions) - { - var realIndex = handler.PositionalViewSelector.GetPosition(itemPosition.SectionIndex, itemPosition.ItemIndex); + var cell = collectionView.CellForItem(NSIndexPath.FromItemSection(realIndex, 0)); - var cell = handler.collectionView.CellForItem(NSIndexPath.FromItemSection(realIndex, 0)); + if (cell is CvCell cvcell) + { + cvcell.PositionInfo.IsSelected = selected; - if (cell is CvCell cvcell) + if (cvcell.VirtualView is IPositionInfo positionInfo) { - cvcell.PositionInfo.IsSelected = selected; - - if (cvcell.VirtualView is IPositionInfo positionInfo) - { - handler.collectionView.InvokeOnMainThread(() => - { - positionInfo.IsSelected = selected; - }); - } + collectionView.InvokeOnMainThread(() => + { + positionInfo.IsSelected = selected; + }); } } - } public static void MapOrientation(VirtualListViewHandler handler, IVirtualListView virtualListView) diff --git a/VirtualListView/Controls/VirtualListView.cs b/VirtualListView/Controls/VirtualListView.cs index 75fefbc..fb693ab 100644 --- a/VirtualListView/Controls/VirtualListView.cs +++ b/VirtualListView/Controls/VirtualListView.cs @@ -231,16 +231,53 @@ void IVirtualListView.Scrolled(ScrolledEventArgs args) OnScrolled?.Invoke(this, args); } + public static readonly BindableProperty ScrolledCommandProperty = + BindableProperty.Create(nameof(ScrolledCommandProperty), typeof(ICommand), typeof(VirtualListView), default); + public ICommand ScrolledCommand { get => (ICommand)GetValue(ScrolledCommandProperty); set => SetValue(ScrolledCommandProperty, value); } - public IReadOnlyList SelectedItems => throw new NotImplementedException(); + public static readonly BindableProperty SelectedItemsProperty = + BindableProperty.Create(nameof(SelectedItemsProperty), typeof(IList), typeof(VirtualListView), Array.Empty(), + propertyChanged: (bobj, ov, nv) => + { + if (bobj is VirtualListView vlv) + { + Console.WriteLine($"SelectedItems: " + string.Join(", ", nv)); + } + }); - public static readonly BindableProperty ScrolledCommandProperty = - BindableProperty.Create(nameof(ScrolledCommandProperty), typeof(ICommand), typeof(VirtualListView), default); + public IList SelectedItems + { + get => (IList)GetValue(SelectedItemsProperty); + set => SetValue(SelectedItemsProperty, value ?? Array.Empty()); + } + + public void DeselectItem(ItemPosition itemPosition) + { + var current = SelectedItems.ToList(); + if (current.Contains(itemPosition)) + { + current.Remove(itemPosition); + SelectedItems = current.ToArray(); + + } + } + + public void SelectItem(ItemPosition itemPosition) + { + var current = SelectedItems; + if (!current.Contains(itemPosition)) + SelectedItems = current.Append(itemPosition).ToArray(); + } + + public void ClearSelectedItems() + { + SelectedItems = Array.Empty(); + } public void InvalidateData() { @@ -309,18 +346,18 @@ public void ViewDetached(PositionInfo position, IView view) public void ViewAttached(PositionInfo position, IView view) => this.AddLogicalChild(view); - public bool IsItemSelected(int sectionIndex, int itemIndex) - => (Handler as VirtualListViewHandler).IsItemSelected(sectionIndex, itemIndex); + //public bool IsItemSelected(int sectionIndex, int itemIndex) + // => (Handler as VirtualListViewHandler).IsItemSelected(sectionIndex, itemIndex); - public void OnSelectedItemsChanged(SelectedItemsChangedEventArgs args) - => this.SelectedItemsChanged?.Invoke(this, args); + public void RaiseSelectedItemsChanged(ItemPosition[] previousSelection, ItemPosition[] newSelection) + => this.SelectedItemsChanged?.Invoke(this, new SelectedItemsChangedEventArgs(previousSelection, newSelection)); - public void SelectItems(params ItemPosition[] paths) - => (Handler as VirtualListViewHandler).SelectItems(paths); + //public void SelectItems(params ItemPosition[] paths) + // => (Handler as VirtualListViewHandler).SelectItems(paths); - public void DeselectItems(params ItemPosition[] paths) - => (Handler as VirtualListViewHandler).DeselectItems(paths); + //public void DeselectItems(params ItemPosition[] paths) + // => (Handler as VirtualListViewHandler).DeselectItems(paths); - public void ClearSelection() - => (Handler as VirtualListViewHandler).ClearSelection(); + //public void ClearSelectedItems() + // => (Handler as VirtualListViewHandler).ClearSelectedItems(); } diff --git a/VirtualListView/IVirtualListView.cs b/VirtualListView/IVirtualListView.cs index 73e4fb0..b578894 100644 --- a/VirtualListView/IVirtualListView.cs +++ b/VirtualListView/IVirtualListView.cs @@ -13,7 +13,8 @@ public interface IVirtualListView : IView IView Footer { get; } event EventHandler SelectedItemsChanged; - void OnSelectedItemsChanged(SelectedItemsChangedEventArgs eventArgs); + + void RaiseSelectedItemsChanged(ItemPosition[] previousSelection, ItemPosition[] newSelection); event EventHandler DataInvalidated; @@ -27,23 +28,19 @@ public interface IVirtualListView : IView SelectionMode SelectionMode { get; } - IReadOnlyList SelectedItems { get; } - ListOrientation Orientation { get; } IView EmptyView { get; } - // IView RefreshView { get; } - - bool IsItemSelected(int sectionIndex, int itemIndex); + IList SelectedItems { get; set; } - void SelectItems(params ItemPosition[] paths); + //bool IsItemSelected(int sectionIndex, int itemIndex); - void DeselectItems(params ItemPosition[] paths); + void SelectItem(ItemPosition path); - void ClearSelection(); + void DeselectItem(ItemPosition path); - //void InvalidateData(); + void ClearSelectedItems(); } public enum ListOrientation diff --git a/VirtualListView/Platforms/Android/RvAdapter.cs b/VirtualListView/Platforms/Android/RvAdapter.cs index 45774dd..696ec37 100644 --- a/VirtualListView/Platforms/Android/RvAdapter.cs +++ b/VirtualListView/Platforms/Android/RvAdapter.cs @@ -57,7 +57,7 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi // The template selector doesn't infer selected properly // so we need to ask the listview which tracks selections about the state info.IsSelected = info.Kind == PositionKind.Item - && (handler?.VirtualView?.IsItemSelected(info.SectionIndex, info.ItemIndex) ?? false); + && (handler?.IsItemSelected(info.SectionIndex, info.ItemIndex) ?? false); if (holder is RvItemHolder itemHolder) { @@ -134,9 +134,9 @@ public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int positionInfo.IsSelected = rvh.PositionInfo.IsSelected; if (rvh.PositionInfo.IsSelected) - handler.VirtualView?.SelectItems(p); + handler?.VirtualView?.SelectItem(p); else - handler.VirtualView?.DeselectItems(p); + handler?.VirtualView?.DeselectItem(p); }); viewHolder.ItemView.SetOnClickListener(clickListener); diff --git a/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs b/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs index 4b754ca..8fa0b46 100644 --- a/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs +++ b/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs @@ -94,33 +94,18 @@ public static void MapSelectionMode(VirtualListViewHandler handler, IVirtualList public static void MapInvalidateData(VirtualListViewHandler handler, IVirtualListView virtualListView, object? parameter) => handler.InvalidateData(); - public static void MapSelectItems(VirtualListViewHandler handler, IVirtualListView virtualListView, object? parameter) + void PlatformUpdateItemSelection(ItemPosition itemPosition, bool selected) { - if (parameter is ItemPosition[] itemPositions) - UpdateSelection(handler, itemPositions, true); - } + var position = PositionalViewSelector.GetPosition(itemPosition.SectionIndex, itemPosition.ItemIndex); - public static void MapDeselectItems(VirtualListViewHandler handler, IVirtualListView virtualListView, object? parameter) - { - if (parameter is ItemPosition[] itemPositions) - UpdateSelection(handler, itemPositions, false); - } + var vh = recyclerView.FindViewHolderForAdapterPosition(position); - static void UpdateSelection(VirtualListViewHandler handler, ItemPosition[] itemPositions, bool selected) - { - foreach (var itemPosition in itemPositions) + if (vh is RvItemHolder rvh) { - var position = handler.PositionalViewSelector.GetPosition(itemPosition.SectionIndex, itemPosition.ItemIndex); - - var vh = handler.recyclerView.FindViewHolderForAdapterPosition(position); - - if (vh is RvItemHolder rvh) - { - rvh.PositionInfo.IsSelected = selected; + rvh.PositionInfo.IsSelected = selected; - if (rvh.ViewContainer?.VirtualView is IPositionInfo viewPositionInfo) - viewPositionInfo.IsSelected = selected; - } + if (rvh.ViewContainer?.VirtualView is IPositionInfo viewPositionInfo) + viewPositionInfo.IsSelected = selected; } } diff --git a/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs b/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs index fd981f3..1571aa7 100644 --- a/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs +++ b/VirtualListView/Platforms/Windows/VirtualListViewHandler.windows.cs @@ -105,7 +105,7 @@ public static void MapSelectItems(VirtualListViewHandler handler, IVirtualListVi { if (parameter is ItemPosition[] items && items != null && items.Length > 0) { - UpdateSelection(handler, items, true); + PlatformUpdateSelection(handler, items, true); } } @@ -113,11 +113,11 @@ public static void MapDeselectItems(VirtualListViewHandler handler, IVirtualList { if (parameter is ItemPosition[] items && items != null && items.Length > 0) { - UpdateSelection(handler, items, false); + PlatformUpdateSelection(handler, items, false); } } - static void UpdateSelection(VirtualListViewHandler handler, ItemPosition[] itemPositions, bool selected) + static void PlatformUpdateSelection(VirtualListViewHandler handler, ItemPosition[] itemPositions, bool selected) { foreach (var itemPosition in itemPositions) { diff --git a/VirtualListView/VirtualListViewHandler.cs b/VirtualListView/VirtualListViewHandler.cs index f531fa2..cdfcb34 100644 --- a/VirtualListView/VirtualListViewHandler.cs +++ b/VirtualListView/VirtualListViewHandler.cs @@ -4,7 +4,7 @@ namespace Microsoft.Maui; public partial class VirtualListViewHandler { - public static PropertyMapper VirtualListViewMapper = new PropertyMapper(VirtualListViewHandler.ViewMapper) + public static new IPropertyMapper ViewMapper = new PropertyMapper(Handlers.ViewHandler.ViewMapper) { [nameof(IVirtualListView.Adapter)] = MapAdapter, [nameof(IVirtualListView.Header)] = MapHeader, @@ -15,20 +15,14 @@ public partial class VirtualListViewHandler [nameof(IVirtualListView.RefreshAccentColor)] = MapRefreshAccentColor, [nameof(IVirtualListView.IsRefreshEnabled)] = MapIsRefreshEnabled, [nameof(IVirtualListView.EmptyView)] = MapEmptyView, + [nameof(IVirtualListView.SelectedItems)] = MapSelectedItems, }; - public static CommandMapper VirtualListViewCommandMapper = new(VirtualListViewHandler.ViewCommandMapper) + public static CommandMapper CommandMapper = new(ViewCommandMapper) { - [nameof(IVirtualListView.SelectItems)] = MapSelectItems, - [nameof(IVirtualListView.DeselectItems)] = MapDeselectItems, }; - public VirtualListViewHandler() : base(VirtualListViewMapper, VirtualListViewCommandMapper) - { - - } - - public VirtualListViewHandler(PropertyMapper mapper = null, CommandMapper commandMapper = null) : base(mapper ?? VirtualListViewMapper, commandMapper ?? VirtualListViewCommandMapper) + public VirtualListViewHandler() : base(ViewMapper, CommandMapper) { } @@ -67,21 +61,6 @@ void Adapter_OnDataInvalidated(object sender, EventArgs e) InvalidateData(); } - readonly object selectedItemsLocker = new object(); - readonly List selectedItems = new List(); - - public IReadOnlyList SelectedItems - { - get - { - if (VirtualView.SelectionMode == SelectionMode.None) - return new List(); - - lock (selectedItemsLocker) - return selectedItems.AsReadOnly(); - } - } - public bool IsItemSelected(int sectionIndex, int itemIndex) { if (VirtualView is null) @@ -90,110 +69,37 @@ public bool IsItemSelected(int sectionIndex, int itemIndex) if (VirtualView.SelectionMode == SelectionMode.None) return false; - lock (selectedItemsLocker) - return selectedItems.Contains(new ItemPosition(sectionIndex, itemIndex)); - } - - public void SelectItems(params ItemPosition[] paths) - { - if (VirtualView is null) - return; - - // Can't select any items in none mode - if (VirtualView.SelectionMode == SelectionMode.None) - return; - - // Can't select multiple items in single mode - if (VirtualView.SelectionMode == SelectionMode.Single && selectedItems.Count > 0) - return; - - // Keep track of previous selection state - var prev = new List(selectedItems); - - lock (selectedItemsLocker) - { - var toAdd = new List(); - - foreach (var path in paths) - { - // Check if the item is already selected - if (selectedItems.Contains(path)) - continue; - - toAdd.Add(path); - } - - foreach (var path in toAdd) - selectedItems.Add(path); - - Invoke(nameof(SelectItems), toAdd.ToArray()); - } - - // Raise event - VirtualView.OnSelectedItemsChanged(new SelectedItemsChangedEventArgs(prev, selectedItems)); + return previousSelections.Contains(new ItemPosition(sectionIndex, itemIndex)); } + ItemPosition[] previousSelections = Array.Empty(); - - public void DeselectItems(params ItemPosition[] paths) + public static void MapSelectedItems(VirtualListViewHandler handler, IVirtualListView virtualListView) { - if (VirtualView is null) - return; - - // Nothing to deselect in none mode - if (VirtualView.SelectionMode == SelectionMode.None) - return; - - // Nothing to deselect in single mode if we have no items selected - if (VirtualView.SelectionMode == SelectionMode.Single && selectedItems.Count <= 0) + if (handler is null) return; - var prev = new List(selectedItems); + var newSelections = virtualListView?.SelectedItems ?? Array.Empty(); - lock (selectedItemsLocker) + // First deselect any previously selected items that aren't in the new set + foreach (var itemPosition in handler.previousSelections) { - var toRemove = new List(); - - foreach (var path in paths) - { - // If our selection doesn't contain the requested item, we can't deselect it - if (!selectedItems.Contains(path)) - continue; - - toRemove.Add(path); - } - - foreach (var path in toRemove) - selectedItems.Remove(path); - - Invoke(nameof(DeselectItems), toRemove.ToArray()); + if (!newSelections.Contains(itemPosition)) + handler.PlatformUpdateItemSelection(itemPosition, false); } - // Raise event - VirtualView.OnSelectedItemsChanged(new SelectedItemsChangedEventArgs(prev, selectedItems)); - } - - public void ClearSelection() - { - if (VirtualView is null) - return; - - if (VirtualView.SelectionMode == SelectionMode.None) - return; - - var prev = new List(selectedItems); - - lock (selectedItemsLocker) + // Set all the new state selected to true + foreach (var itemPosition in newSelections) { - var toRemove = selectedItems.ToArray(); - selectedItems.Clear(); + if (!handler.previousSelections.Contains(itemPosition)) + handler.PlatformUpdateItemSelection(itemPosition, true); + } - UpdateSelection(this, toRemove, false); + var prev = handler.previousSelections.ToArray(); - Invoke(nameof(ClearSelection), toRemove); - } + // Keep track of the new state for next time it changes + handler.previousSelections = newSelections.ToArray(); - // Raise event - //VirtualView.OnSelectedItemsChanged(new SelectedItemsChangedEventArgs(prev, selectedItems)); + handler.VirtualView.RaiseSelectedItemsChanged(prev, newSelections.ToArray()); } } \ No newline at end of file From 7b4889b9427b2b3ec03c87c88a4aa999d5b2d866 Mon Sep 17 00:00:00 2001 From: redth Date: Thu, 27 Jul 2023 15:48:17 -0400 Subject: [PATCH 02/14] Fix bindable property names --- VirtualListView/Controls/VirtualListView.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VirtualListView/Controls/VirtualListView.cs b/VirtualListView/Controls/VirtualListView.cs index fb693ab..0efc0b5 100644 --- a/VirtualListView/Controls/VirtualListView.cs +++ b/VirtualListView/Controls/VirtualListView.cs @@ -3,7 +3,7 @@ namespace Microsoft.Maui.Controls; -public partial class VirtualListView : View, IVirtualListView, IVirtualListViewSelector, IVisualTreeElement +public partial class VirtualListView : View, IVirtualListView, IVirtualListViewSelector { public static readonly BindableProperty PositionInfoProperty = BindableProperty.CreateAttached( nameof(PositionInfo), @@ -232,7 +232,7 @@ void IVirtualListView.Scrolled(ScrolledEventArgs args) } public static readonly BindableProperty ScrolledCommandProperty = - BindableProperty.Create(nameof(ScrolledCommandProperty), typeof(ICommand), typeof(VirtualListView), default); + BindableProperty.Create(nameof(ScrolledCommand), typeof(ICommand), typeof(VirtualListView), default); public ICommand ScrolledCommand { @@ -241,7 +241,7 @@ public ICommand ScrolledCommand } public static readonly BindableProperty SelectedItemsProperty = - BindableProperty.Create(nameof(SelectedItemsProperty), typeof(IList), typeof(VirtualListView), Array.Empty(), + BindableProperty.Create(nameof(SelectedItems), typeof(IList), typeof(VirtualListView), Array.Empty(), propertyChanged: (bobj, ov, nv) => { if (bobj is VirtualListView vlv) From 186473b26dab5d457a76db5e52562a0f95d4e7d0 Mon Sep 17 00:00:00 2001 From: redth Date: Thu, 27 Jul 2023 15:48:48 -0400 Subject: [PATCH 03/14] Fix selection logic --- .../ObservableCollectionPage.xaml.cs | 5 ----- .../SectionedAdapterPage.xaml.cs | 12 ++++++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs b/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs index 66c9713..4062d0b 100644 --- a/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs +++ b/Sample/VirtualListViewSample/ObservableCollectionPage.xaml.cs @@ -17,15 +17,11 @@ public ObservableCollectionPage() } vlv.Adapter = Adapter; - - //vlv.SetBinding(VirtualListView.SelectedItemsProperty, new Binding("SelectedItems", BindingMode.TwoWay, source: this)); } public ObservableCollectionAdapter Adapter { get; set; } public ObservableCollection Items = new(); - //public ItemPosition[] SelectedItems = Array.Empty(); - protected override void OnAppearing() { base.OnAppearing(); @@ -61,6 +57,5 @@ private void vlv_SelectedItemsChanged(object sender, SelectedItemsChangedEventAr Items.Remove(item); } - } } \ No newline at end of file diff --git a/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs b/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs index 01cc921..12e3944 100644 --- a/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs +++ b/Sample/VirtualListViewSample/SectionedAdapterPage.xaml.cs @@ -38,13 +38,13 @@ private void Button_Clicked(object sender, EventArgs e) private void vlv_SelectedItemsChanged(object sender, SelectedItemsChangedEventArgs e) { - var item = e.NewSelection?.FirstOrDefault(); - - if (item != null) + if (e.NewSelection.Any()) { - Adapter.RemoveItem(item.Value.SectionIndex, item.Value.ItemIndex); - } + var item = e.NewSelection.First(); - (sender as VirtualListView).SelectedItems = null; + Adapter.RemoveItem(item.SectionIndex, item.ItemIndex); + vlv.ClearSelectedItems(); + } + } } From ad41c4e84ddbf4f9b3cb9139411f350fd7745072 Mon Sep 17 00:00:00 2001 From: redth Date: Thu, 27 Jul 2023 16:20:05 -0400 Subject: [PATCH 04/14] Clean up events --- .../VirtualListViewHandler.ios.maccatalyst.cs | 2 +- VirtualListView/Controls/VirtualListView.cs | 77 ++++--------------- VirtualListView/IVirtualListView.cs | 18 ++--- .../Android/VirtualListViewHandler.android.cs | 2 +- VirtualListView/VirtualListViewHandler.cs | 2 - 5 files changed, 24 insertions(+), 77 deletions(-) diff --git a/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs b/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs index cd62df0..4b58daf 100644 --- a/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs +++ b/VirtualListView/Apple/VirtualListViewHandler.ios.maccatalyst.cs @@ -67,7 +67,7 @@ protected override void ConnectHandler(UICollectionView nativeView) cvdelegate = new CvDelegate(this, collectionView); cvdelegate.ScrollHandler = (x, y) => - VirtualView?.Scrolled(new ScrolledEventArgs(x, y)); + VirtualView?.Scrolled(x, y); collectionView.DataSource = dataSource; collectionView.Delegate = cvdelegate; diff --git a/VirtualListView/Controls/VirtualListView.cs b/VirtualListView/Controls/VirtualListView.cs index 0efc0b5..e25c679 100644 --- a/VirtualListView/Controls/VirtualListView.cs +++ b/VirtualListView/Controls/VirtualListView.cs @@ -109,7 +109,7 @@ public Maui.SelectionMode SelectionMode public static readonly BindableProperty SelectionModeProperty = BindableProperty.Create(nameof(SelectionMode), typeof(Maui.SelectionMode), typeof(VirtualListView), Maui.SelectionMode.None); - public event EventHandler SelectedItemsChanged; + public event EventHandler OnSelectedItemsChanged; public event EventHandler OnRefresh; @@ -183,50 +183,19 @@ public View EmptyView IView IVirtualListView.EmptyView => EmptyView; - - //public View RefreshView - //{ - // get => (View)GetValue(RefreshViewProperty); - // set => SetValue(RefreshViewProperty, value); - //} - - //public static readonly BindableProperty RefreshViewProperty = - // BindableProperty.Create(nameof(EmptyView), typeof(View), typeof(VirtualListView), null, - // propertyChanged: (bobj, oldValue, newValue) => - // { - // if (bobj is VirtualListView virtualListView) - // { - // if (oldValue is IView oldView) - // virtualListView.RemoveLogicalChild(oldView); - - // if (newValue is IView newView) - // virtualListView.AddLogicalChild(newView); - // } - // }); - - //IView IVirtualListView.RefreshView => RefreshView; - - - - public IVirtualListViewSelector ViewSelector => this; public IView Header => GlobalHeader; public IView Footer => GlobalFooter; - - - public event EventHandler DataInvalidated; - - public event EventHandler OnScrolled; - void IVirtualListView.Scrolled(ScrolledEventArgs args) + public void Scrolled(double x, double y) { + var args = new ScrolledEventArgs(x, y); + if (ScrolledCommand != null && ScrolledCommand.CanExecute(args)) - { ScrolledCommand.Execute(args); - } OnScrolled?.Invoke(this, args); } @@ -242,11 +211,13 @@ public ICommand ScrolledCommand public static readonly BindableProperty SelectedItemsProperty = BindableProperty.Create(nameof(SelectedItems), typeof(IList), typeof(VirtualListView), Array.Empty(), - propertyChanged: (bobj, ov, nv) => + propertyChanged: (bindableObj, oldValue, newValue) => { - if (bobj is VirtualListView vlv) + if (bindableObj is VirtualListView vlv + && oldValue is IList oldSelection + && newValue is IList newSelection) { - Console.WriteLine($"SelectedItems: " + string.Join(", ", nv)); + vlv.RaiseSelectedItemsChanged(oldSelection.ToArray(), newSelection.ToArray()); } }); @@ -263,7 +234,7 @@ public void DeselectItem(ItemPosition itemPosition) { current.Remove(itemPosition); SelectedItems = current.ToArray(); - + } } @@ -279,13 +250,6 @@ public void ClearSelectedItems() SelectedItems = Array.Empty(); } - public void InvalidateData() - { - (Handler as VirtualListViewHandler)?.InvalidateData(); - - DataInvalidated?.Invoke(this, new EventArgs()); - } - public bool SectionHasHeader(int sectionIndex) => SectionHeaderTemplateSelector != null || SectionHeaderTemplate != null; @@ -293,8 +257,9 @@ public bool SectionHasFooter(int sectionIndex) => SectionFooterTemplateSelector != null || SectionFooterTemplate != null; public IView CreateView(PositionInfo position, object data) - => position.Kind switch { - PositionKind.Item => + => position.Kind switch + { + PositionKind.Item => ItemTemplateSelector?.SelectTemplate(data, position.SectionIndex, position.ItemIndex)?.CreateContent() as View ?? ItemTemplate?.CreateContent() as View, PositionKind.SectionHeader => @@ -346,18 +311,6 @@ public void ViewDetached(PositionInfo position, IView view) public void ViewAttached(PositionInfo position, IView view) => this.AddLogicalChild(view); - //public bool IsItemSelected(int sectionIndex, int itemIndex) - // => (Handler as VirtualListViewHandler).IsItemSelected(sectionIndex, itemIndex); - - public void RaiseSelectedItemsChanged(ItemPosition[] previousSelection, ItemPosition[] newSelection) - => this.SelectedItemsChanged?.Invoke(this, new SelectedItemsChangedEventArgs(previousSelection, newSelection)); - - //public void SelectItems(params ItemPosition[] paths) - // => (Handler as VirtualListViewHandler).SelectItems(paths); - - //public void DeselectItems(params ItemPosition[] paths) - // => (Handler as VirtualListViewHandler).DeselectItems(paths); - - //public void ClearSelectedItems() - // => (Handler as VirtualListViewHandler).ClearSelectedItems(); + void RaiseSelectedItemsChanged(ItemPosition[] previousSelection, ItemPosition[] newSelection) + => this.OnSelectedItemsChanged?.Invoke(this, new SelectedItemsChangedEventArgs(previousSelection, newSelection)); } diff --git a/VirtualListView/IVirtualListView.cs b/VirtualListView/IVirtualListView.cs index b578894..25e0840 100644 --- a/VirtualListView/IVirtualListView.cs +++ b/VirtualListView/IVirtualListView.cs @@ -12,11 +12,15 @@ public interface IVirtualListView : IView IView Footer { get; } - event EventHandler SelectedItemsChanged; + event EventHandler OnScrolled; - void RaiseSelectedItemsChanged(ItemPosition[] previousSelection, ItemPosition[] newSelection); + void Scrolled(double x, double y); - event EventHandler DataInvalidated; + SelectionMode SelectionMode { get; } + + IList SelectedItems { get; set; } + + event EventHandler OnSelectedItemsChanged; Color RefreshAccentColor { get; } @@ -24,18 +28,10 @@ public interface IVirtualListView : IView bool IsRefreshEnabled { get; } - void Scrolled(ScrolledEventArgs args); - - SelectionMode SelectionMode { get; } - ListOrientation Orientation { get; } IView EmptyView { get; } - IList SelectedItems { get; set; } - - //bool IsItemSelected(int sectionIndex, int itemIndex); - void SelectItem(ItemPosition path); void DeselectItem(ItemPosition path); diff --git a/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs b/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs index 8fa0b46..8d30f5f 100644 --- a/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs +++ b/VirtualListView/Platforms/Android/VirtualListViewHandler.android.cs @@ -53,7 +53,7 @@ protected override void ConnectHandler(FrameLayout nativeView) var x = Context.FromPixels(dx); var y = Context.FromPixels(dy); - VirtualView?.Scrolled(new ScrolledEventArgs(x, y)); + VirtualView?.Scrolled(x, y); })); recyclerView.SetLayoutManager(layoutManager); diff --git a/VirtualListView/VirtualListViewHandler.cs b/VirtualListView/VirtualListViewHandler.cs index cdfcb34..d831fbf 100644 --- a/VirtualListView/VirtualListViewHandler.cs +++ b/VirtualListView/VirtualListViewHandler.cs @@ -99,7 +99,5 @@ public static void MapSelectedItems(VirtualListViewHandler handler, IVirtualList // Keep track of the new state for next time it changes handler.previousSelections = newSelections.ToArray(); - - handler.VirtualView.RaiseSelectedItemsChanged(prev, newSelections.ToArray()); } } \ No newline at end of file From 24edaa7e9ee495e22a5b68f09cd313518da3a42e Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 28 Jul 2023 10:53:18 -0400 Subject: [PATCH 05/14] Fix event renames --- Sample/VirtualListViewSample/MusicLibraryPage.xaml | 2 +- Sample/VirtualListViewSample/ObservableCollectionPage.xaml | 2 +- Sample/VirtualListViewSample/SectionedAdapterPage.xaml | 2 +- VirtualListView/Controls/VirtualListView.cs | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Sample/VirtualListViewSample/MusicLibraryPage.xaml b/Sample/VirtualListViewSample/MusicLibraryPage.xaml index 1996cbc..45ad698 100644 --- a/Sample/VirtualListViewSample/MusicLibraryPage.xaml +++ b/Sample/VirtualListViewSample/MusicLibraryPage.xaml @@ -23,7 +23,7 @@ RefreshCommand="{Binding RefreshCommand}" Adapter="{Binding Adapter}" SelectionMode="Multiple" - SelectedItemsChanged="VirtualListView_SelectedItemsChanged" + OnSelectedItemsChanged="VirtualListView_SelectedItemsChanged" ItemTemplateSelector="{StaticResource itemTemplateSelector}"> diff --git a/Sample/VirtualListViewSample/ObservableCollectionPage.xaml b/Sample/VirtualListViewSample/ObservableCollectionPage.xaml index 92dbd36..d09a2a3 100644 --- a/Sample/VirtualListViewSample/ObservableCollectionPage.xaml +++ b/Sample/VirtualListViewSample/ObservableCollectionPage.xaml @@ -11,7 +11,7 @@ Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" x:Name="vlv" - SelectedItemsChanged="vlv_SelectedItemsChanged" + OnSelectedItemsChanged="vlv_SelectedItemsChanged" SelectionMode="Multiple"> diff --git a/Sample/VirtualListViewSample/SectionedAdapterPage.xaml b/Sample/VirtualListViewSample/SectionedAdapterPage.xaml index e25ace7..3e7ed68 100644 --- a/Sample/VirtualListViewSample/SectionedAdapterPage.xaml +++ b/Sample/VirtualListViewSample/SectionedAdapterPage.xaml @@ -11,7 +11,7 @@ Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" x:Name="vlv" - SelectedItemsChanged="vlv_SelectedItemsChanged" + OnSelectedItemsChanged="vlv_SelectedItemsChanged" SelectionMode="Single">