Skip to content

Commit 9dca5bd

Browse files
authored
Improved rectangle selection performance (#7366)
1 parent c0ae5da commit 9dca5bd

File tree

1 file changed

+44
-15
lines changed

1 file changed

+44
-15
lines changed

src/Files/UserControls/Selection/RectangleSelection_ListViewBase.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using Microsoft.Toolkit.Uwp.UI;
56
using Windows.Foundation;
67
using Windows.System;
78
using Windows.UI.Xaml;
@@ -16,7 +17,7 @@ public class RectangleSelection_ListViewBase : RectangleSelection
1617
private ListViewBase uiElement;
1718
private ScrollViewer scrollViewer;
1819
private SelectionChangedEventHandler selectionChanged;
19-
20+
private DispatcherQueueTimer timer;
2021
private Point originDragPoint;
2122
private Dictionary<object, System.Drawing.Rectangle> itemsPosition;
2223
private List<object> prevSelectedItems;
@@ -28,6 +29,7 @@ public RectangleSelection_ListViewBase(ListViewBase uiElement, Rectangle selecti
2829
this.selectionRectangle = selectionRectangle;
2930
this.selectionChanged = selectionChanged;
3031
itemsPosition = new Dictionary<object, System.Drawing.Rectangle>();
32+
timer = DispatcherQueue.GetForCurrentThread().CreateTimer();
3133
InitEvents(null, null);
3234
}
3335

@@ -58,19 +60,6 @@ private void RectangleSelection_PointerMoved(object sender, PointerRoutedEventAr
5860
base.DrawRectangle(currentPoint, originDragPointShifted, uiElement);
5961
// Selected area considering scrolled offset
6062
var rect = new System.Drawing.Rectangle((int)Canvas.GetLeft(selectionRectangle), (int)Math.Min(originDragPoint.Y, currentPoint.Position.Y + verticalOffset), (int)selectionRectangle.Width, (int)Math.Abs(originDragPoint.Y - (currentPoint.Position.Y + verticalOffset)));
61-
foreach (var item in uiElement.Items.ToList().Except(itemsPosition.Keys))
62-
{
63-
var listViewItem = (FrameworkElement)uiElement.ContainerFromItem(item); // Get ListViewItem
64-
if (listViewItem == null)
65-
{
66-
continue; // Element is not loaded (virtualized list)
67-
}
68-
69-
var gt = listViewItem.TransformToVisual(uiElement);
70-
var itemStartPoint = gt.TransformPoint(new Point(0, verticalOffset)); // Get item position relative to the top of the list (considering scrolled offset)
71-
var itemRect = new System.Drawing.Rectangle((int)itemStartPoint.X, (int)itemStartPoint.Y, (int)listViewItem.ActualWidth, (int)listViewItem.ActualHeight);
72-
itemsPosition[item] = itemRect;
73-
}
7463

7564
foreach (var item in itemsPosition.ToList())
7665
{
@@ -108,17 +97,29 @@ private void RectangleSelection_PointerMoved(object sender, PointerRoutedEventAr
10897

10998
private void RectangleSelection_PointerPressed(object sender, PointerRoutedEventArgs e)
11099
{
100+
if (scrollViewer == null)
101+
{
102+
return;
103+
}
104+
111105
itemsPosition.Clear();
106+
107+
scrollViewer.ViewChanged -= ScrollViewer_ViewChanged;
108+
scrollViewer.ViewChanged += ScrollViewer_ViewChanged;
109+
112110
originDragPoint = new Point(e.GetCurrentPoint(uiElement).Position.X, e.GetCurrentPoint(uiElement).Position.Y); // Initial drag point relative to the topleft corner
113111
prevSelectedItems = uiElement.SelectedItems.Cast<object>().ToList(); // Save current selected items
114-
var verticalOffset = scrollViewer?.VerticalOffset ?? 0;
112+
113+
var verticalOffset = scrollViewer.VerticalOffset;
115114
originDragPoint.Y += verticalOffset; // Initial drag point relative to the top of the list (considering scrolled offset)
116115
if (!e.GetCurrentPoint(uiElement).Properties.IsLeftButtonPressed || e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
117116
{
118117
// Trigger only on left click, do not trigger with touch
119118
return;
120119
}
121120

121+
FetchItemsPosition();
122+
122123
selectionStrategy = e.KeyModifiers.HasFlag(VirtualKeyModifiers.Control) ?
123124
new InvertPreviousItemSelectionStrategy(uiElement.SelectedItems, prevSelectedItems) :
124125
e.KeyModifiers.HasFlag(VirtualKeyModifiers.Shift) ?
@@ -138,13 +139,41 @@ private void RectangleSelection_PointerPressed(object sender, PointerRoutedEvent
138139
selectionState = SelectionState.Starting;
139140
}
140141

142+
private void FetchItemsPosition()
143+
{
144+
var verticalOffset = scrollViewer.VerticalOffset;
145+
foreach (var item in uiElement.Items.ToList().Except(itemsPosition.Keys))
146+
{
147+
var listViewItem = (FrameworkElement)uiElement.ContainerFromItem(item); // Get ListViewItem
148+
if (listViewItem == null)
149+
{
150+
continue; // Element is not loaded (virtualized list)
151+
}
152+
153+
var gt = listViewItem.TransformToVisual(uiElement);
154+
var itemStartPoint = gt.TransformPoint(new Point(0, verticalOffset)); // Get item position relative to the top of the list (considering scrolled offset)
155+
var itemRect = new System.Drawing.Rectangle((int)itemStartPoint.X, (int)itemStartPoint.Y, (int)listViewItem.ActualWidth, (int)listViewItem.ActualHeight);
156+
itemsPosition[item] = itemRect;
157+
}
158+
}
159+
160+
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
161+
{
162+
if (!timer.IsRunning)
163+
{
164+
timer.Debounce(FetchItemsPosition, TimeSpan.FromMilliseconds(1000));
165+
}
166+
}
167+
141168
private void RectangleSelection_PointerReleased(object sender, PointerRoutedEventArgs e)
142169
{
143170
Canvas.SetLeft(selectionRectangle, 0);
144171
Canvas.SetTop(selectionRectangle, 0);
145172
selectionRectangle.Width = 0;
146173
selectionRectangle.Height = 0;
147174
uiElement.PointerMoved -= RectangleSelection_PointerMoved;
175+
176+
scrollViewer.ViewChanged -= ScrollViewer_ViewChanged;
148177
uiElement.ReleasePointerCapture(e.Pointer);
149178
if (selectionChanged != null)
150179
{

0 commit comments

Comments
 (0)