-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExportView.xaml.cs
305 lines (257 loc) · 11.8 KB
/
ExportView.xaml.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using MO2ExportImport.ViewModels;
namespace MO2ExportImport.Views
{
public partial class ExportView : UserControl
{
private bool isScrolling = false;
private HashSet<object> manuallySelectedItems = new HashSet<object>();
private DispatcherTimer textChangedTimer;
private DateTime _lastHighlightUpdate = DateTime.MinValue;
private DispatcherTimer _highlightThrottleTimer;
private bool _pendingHighlightUpdate = false;
private bool _shouldClearExisting = false;
private DispatcherTimer _resizeThrottleTimer;
private DispatcherTimer _pleaseWaitTimer;
public ExportView()
{
InitializeComponent();
_highlightThrottleTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(50)
};
_highlightThrottleTimer.Tick += (s, e) =>
{
_highlightThrottleTimer.Stop();
if (_pendingHighlightUpdate)
{
UpdateHighlightPositions(_shouldClearExisting);
_lastHighlightUpdate = DateTime.Now;
_pendingHighlightUpdate = false;
_shouldClearExisting = false; // Reset the flag after the update
}
};
ModsListBox.SelectionChanged += ModsListBox_SelectionChanged;
// Initialize the DispatcherTimer
textChangedTimer = new DispatcherTimer();
textChangedTimer.Interval = TimeSpan.FromSeconds(0.05); // Set the interval
textChangedTimer.Tick += TextChangedTimer_Tick;
this.SizeChanged += ExportView_SizeChanged;
_resizeThrottleTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(25) // Adjust as needed
};
_resizeThrottleTimer.Tick += ResizeThrottleTimer_Tick;
_pleaseWaitTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(100) // Adjust as needed
};
_pleaseWaitTimer.Tick += PleaseWaitTimer_Tick;
}
private void ModsListBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
isScrolling = true;
}
private void ModsListBox_ScrollCompleted(object sender, RoutedEventArgs e)
{
isScrolling = false;
}
private void ModsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// prevent erroneous selection of ListBox items in large lists due to Virtualization
if (isScrolling)
{
return;
}
if (DataContext is ExportViewModel viewModel)
{
// Check if the selection was changed programmatically
if (viewModel.IsLoadingList)
{
return; // Ignore the event if triggered programmatically
}
// Determine if the selection was modified (e.g., Ctrl+click or Shift+click)
bool isModifiedSelection = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl) ||
Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) ||
Mouse.LeftButton == MouseButtonState.Pressed && Keyboard.Modifiers != ModifierKeys.None;
// Calculate the value for shouldClearExisting
bool shouldClearExisting = !isModifiedSelection;
// Update the selected item count for the view model
// If the selection is modified, do not clear existing rectangles
if (e.AddedItems.Count > 0 || e.RemovedItems.Count > 0)
{
if ((DateTime.Now - _lastHighlightUpdate).TotalMilliseconds > 50)
{
// If 50ms have passed, update immediately
_lastHighlightUpdate = DateTime.Now;
UpdateHighlightPositions(shouldClearExisting);
PleaseWaitText.Visibility = Visibility.Collapsed;
}
else
{
// Otherwise, flag that an update is pending
_pendingHighlightUpdate = true;
// If shouldClearExisting is true at any time within the 50ms period, we want to preserve that state
_shouldClearExisting = _shouldClearExisting || shouldClearExisting;
if (!_highlightThrottleTimer.IsEnabled)
{
_highlightThrottleTimer.Start();
}
}
}
viewModel.UpdateSelectedCount();
}
}
private void UpdateHighlightPositions(bool clearExisting = true)
{
// Check if FilterTextBox.Text is empty; if not, do not place marks
if (!string.IsNullOrEmpty(FilterTextBox.Text))
{
return;
}
// Reset the PleaseWait timer
_pleaseWaitTimer.Stop();
if (clearExisting)
{
HighlightCanvas.Children.Clear();
}
// Get the ScrollViewer inside the ListBox
var scrollViewer = GetScrollViewer(ModsListBox);
if (scrollViewer == null)
return;
// Calculate the scrollbar button height (same as scrollbar width)
double scrollbarButtonHeight = SystemParameters.VerticalScrollBarWidth;
// Calculate the adjusted scrollable height
double adjustedScrollableHeight = scrollViewer.ScrollableHeight + (2 * scrollbarButtonHeight);
// Calculate the average height of visible items
double totalVisibleHeight = 0;
int visibleItemCount = 0;
foreach (var item in ModsListBox.Items)
{
var container = ModsListBox.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
if (container != null)
{
totalVisibleHeight += container.ActualHeight;
visibleItemCount++;
}
}
// Calculate the average item height
// Fallback to an estimated height if no items are visible
double averageItemHeight;
if (visibleItemCount > 0)
{
averageItemHeight = totalVisibleHeight / visibleItemCount;
}
else
{
// Create a virtual StackPanel to mimic the DataTemplate
var virtualStackPanel = new StackPanel { Orientation = Orientation.Horizontal };
var virtualCheckBox = new CheckBox { IsChecked = true, IsEnabled = false, Margin = new Thickness(0, 0, 10, 0) };
var virtualTextBlock = new TextBlock { Text = "Sample Text", VerticalAlignment = VerticalAlignment.Center };
virtualStackPanel.Children.Add(virtualCheckBox);
virtualStackPanel.Children.Add(virtualTextBlock);
virtualStackPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
averageItemHeight = virtualStackPanel.DesiredSize.Height;
}
// Estimate the total height of all items
double totalHeight = averageItemHeight * ModsListBox.Items.Count;
// Iterate over each SelectedItem in the ListBox
foreach (var item in ModsListBox.SelectedItems)
{
// Calculate the cumulative height up to this item using the average height
int itemIndex = ModsListBox.Items.IndexOf(item);
double cumulativeHeight = averageItemHeight * itemIndex;
// Calculate the ratio of the selected item's position relative to the total theoretical height
double selectedPositionRatio = cumulativeHeight / totalHeight;
// Calculate the position of the rectangle relative to the adjusted scrollable height
double rectanglePosition = selectedPositionRatio * (HighlightCanvas.ActualHeight - (2 * scrollbarButtonHeight)) + scrollbarButtonHeight;
// Create and place the rectangle
double rectangleHeight = 2; // Thickness of the rectangle
double scrollbarWidth = scrollbarButtonHeight; // Use the scrollbar width for the rectangle width
double canvasWidth = HighlightCanvas.ActualWidth;
double lineLeft = canvasWidth - scrollbarWidth + scrollbarWidth / 2;
var line = new Rectangle
{
Width = scrollbarWidth / 2,
Height = rectangleHeight,
Fill = Brushes.Blue
};
Canvas.SetLeft(line, lineLeft);
Canvas.SetTop(line, rectanglePosition - (rectangleHeight / 2)); // Adjust for centering the rectangle
HighlightCanvas.Children.Add(line);
}
// Reset the PleaseWait timer
_pleaseWaitTimer.Start();
}
private ScrollViewer GetScrollViewer(DependencyObject o)
{
if (o is ScrollViewer)
{
return (ScrollViewer)o;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
{
var child = VisualTreeHelper.GetChild(o, i);
var result = GetScrollViewer(child);
if (result != null)
return result;
}
return null;
}
private void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
// Reset the timer whenever the text changes
textChangedTimer.Stop();
textChangedTimer.Start();
if (string.IsNullOrEmpty(FilterTextBox.Text))
{
// FilterText is empty, re-add rectangles for all selected items
UpdateHighlightPositions(false);
}
else
{
// FilterText is not empty, remove all rectangles
HighlightCanvas.Children.Clear();
}
}
private void TextChangedTimer_Tick(object sender, EventArgs e)
{
// Stop the timer
textChangedTimer.Stop();
// Place your logic here to handle the text change
if (string.IsNullOrEmpty(FilterTextBox.Text))
{
// FilterText is empty, re-add rectangles for all selected items
UpdateHighlightPositions(false);
}
else
{
// FilterText is not empty, remove all rectangles
HighlightCanvas.Children.Clear();
}
}
private void ExportView_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Start the throttle timer when a resize is detected
if (!_resizeThrottleTimer.IsEnabled)
{
_resizeThrottleTimer.Start();
}
}
private void ResizeThrottleTimer_Tick(object sender, EventArgs e)
{
_resizeThrottleTimer.Stop(); // Stop the timer after one tick
UpdateHighlightPositions(true); // Update the highlight positions based on the new size
}
private void PleaseWaitTimer_Tick(object sender, EventArgs e)
{
_pleaseWaitTimer.Stop(); // Stop the timer after one tick
PleaseWaitText.Visibility = Visibility.Collapsed;
}
}
}