Skip to content

Commit c686da0

Browse files
committed
Improve UI responsive while enumerating
1 parent 649cb3e commit c686da0

File tree

2 files changed

+93
-75
lines changed

2 files changed

+93
-75
lines changed

Files/Filesystem/StorageEnumerators/UniversalStorageEnumerator.cs

+53-42
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
using Files.Filesystem.StorageItems;
44
using Files.Helpers;
55
using Files.Views.LayoutModes;
6+
using Microsoft.Toolkit.Uwp;
67
using System;
78
using System.Collections.Generic;
89
using System.Diagnostics;
910
using System.IO;
1011
using System.Linq;
1112
using System.Threading;
1213
using System.Threading.Tasks;
14+
using Windows.ApplicationModel.Core;
1315
using Windows.Storage;
1416
using Windows.Storage.FileProperties;
1517
using Windows.UI.Xaml.Media.Imaging;
@@ -186,71 +188,80 @@ CancellationToken cancellationToken
186188
var itemFolderImgVis = false;
187189
var itemFileExtension = file.FileType;
188190

189-
BitmapImage icon = new BitmapImage();
190-
byte[] iconData = null;
191-
bool itemThumbnailImgVis;
192-
bool itemEmptyImgVis;
193-
194-
if (sourcePageType != typeof(GridViewBrowser))
191+
async Task<(BitmapImage Icon, byte[] IconData, bool ThumbnailImgVis, bool EmptyImgVis)> GetIconAsync()
195192
{
196-
try
193+
BitmapImage icon = new BitmapImage();
194+
byte[] iconData = null;
195+
bool itemThumbnailImgVis;
196+
bool itemEmptyImgVis;
197+
198+
if (sourcePageType != typeof(GridViewBrowser))
197199
{
198-
using var itemThumbnailImg = suppressThumbnailLoading ? null :
199-
await file.GetThumbnailAsync(ThumbnailMode.ListView, 40, ThumbnailOptions.UseCurrentScale);
200-
if (itemThumbnailImg != null)
200+
try
201201
{
202-
itemEmptyImgVis = false;
203-
itemThumbnailImgVis = true;
204-
icon.DecodePixelWidth = 40;
205-
icon.DecodePixelHeight = 40;
206-
await icon.SetSourceAsync(itemThumbnailImg);
207-
iconData = await itemThumbnailImg.ToByteArrayAsync();
202+
using var itemThumbnailImg = suppressThumbnailLoading ? null :
203+
await file.GetThumbnailAsync(ThumbnailMode.ListView, 40, ThumbnailOptions.UseCurrentScale);
204+
if (itemThumbnailImg != null)
205+
{
206+
itemEmptyImgVis = false;
207+
itemThumbnailImgVis = true;
208+
icon.DecodePixelWidth = 40;
209+
icon.DecodePixelHeight = 40;
210+
await icon.SetSourceAsync(itemThumbnailImg);
211+
iconData = await itemThumbnailImg.ToByteArrayAsync();
212+
}
213+
else
214+
{
215+
itemEmptyImgVis = true;
216+
itemThumbnailImgVis = false;
217+
}
208218
}
209-
else
219+
catch
210220
{
211221
itemEmptyImgVis = true;
212222
itemThumbnailImgVis = false;
223+
// Catch here to avoid crash
213224
}
214225
}
215-
catch
216-
{
217-
itemEmptyImgVis = true;
218-
itemThumbnailImgVis = false;
219-
// Catch here to avoid crash
220-
}
221-
}
222-
else
223-
{
224-
try
226+
else
225227
{
226-
using var itemThumbnailImg = suppressThumbnailLoading ? null :
227-
await file.GetThumbnailAsync(ThumbnailMode.ListView, 80, ThumbnailOptions.UseCurrentScale);
228-
if (itemThumbnailImg != null)
228+
try
229229
{
230-
itemEmptyImgVis = false;
231-
itemThumbnailImgVis = true;
232-
icon.DecodePixelWidth = 80;
233-
icon.DecodePixelHeight = 80;
234-
await icon.SetSourceAsync(itemThumbnailImg);
235-
iconData = await itemThumbnailImg.ToByteArrayAsync();
230+
using var itemThumbnailImg = suppressThumbnailLoading ? null :
231+
await file.GetThumbnailAsync(ThumbnailMode.ListView, 80, ThumbnailOptions.UseCurrentScale);
232+
if (itemThumbnailImg != null)
233+
{
234+
itemEmptyImgVis = false;
235+
itemThumbnailImgVis = true;
236+
icon.DecodePixelWidth = 80;
237+
icon.DecodePixelHeight = 80;
238+
await icon.SetSourceAsync(itemThumbnailImg);
239+
iconData = await itemThumbnailImg.ToByteArrayAsync();
240+
}
241+
else
242+
{
243+
itemEmptyImgVis = true;
244+
itemThumbnailImgVis = false;
245+
}
236246
}
237-
else
247+
catch
238248
{
239249
itemEmptyImgVis = true;
240250
itemThumbnailImgVis = false;
241251
}
242252
}
243-
catch
244-
{
245-
itemEmptyImgVis = true;
246-
itemThumbnailImgVis = false;
247-
}
253+
254+
return (icon, iconData, itemThumbnailImgVis, itemEmptyImgVis);
248255
}
256+
249257
if (cancellationToken.IsCancellationRequested)
250258
{
251259
return null;
252260
}
253261

262+
await CoreApplication.MainView.Dispatcher.YieldAsync(Windows.UI.Core.CoreDispatcherPriority.Normal);
263+
var (icon, iconData, itemThumbnailImgVis, itemEmptyImgVis) = await GetIconAsync();
264+
254265
if (file.Name.EndsWith(".lnk") || file.Name.EndsWith(".url"))
255266
{
256267
// This shouldn't happen, StorageFile api does not support shortcuts

Files/ViewModels/ItemViewModel.cs

+40-33
Original file line numberDiff line numberDiff line change
@@ -489,19 +489,19 @@ public async Task ApplyFilesAndFoldersChangesAsync()
489489
{
490490
if (filesAndFolders == null || filesAndFolders.Count == 0)
491491
{
492-
Action action = () =>
492+
void ClearDisplay()
493493
{
494494
FilesAndFolders.Clear();
495495
UpdateEmptyTextType();
496496
DirectoryInfoUpdated?.Invoke(this, EventArgs.Empty);
497-
};
497+
}
498498
if (CoreApplication.MainView.DispatcherQueue.HasThreadAccess)
499499
{
500-
action();
500+
ClearDisplay();
501501
}
502502
else
503503
{
504-
await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(action);
504+
await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(ClearDisplay);
505505
}
506506
return;
507507
}
@@ -517,7 +517,7 @@ public async Task ApplyFilesAndFoldersChangesAsync()
517517
// After calling BeginBulkOperation, ObservableCollection.CollectionChanged is suppressed
518518
// so modifies to FilesAndFolders won't trigger UI updates, hence below operations can be
519519
// run safely without needs of dispatching to UI thread
520-
Action applyChangesAction = () =>
520+
void ApplyChanges()
521521
{
522522
var startIndex = -1;
523523
var tempList = new List<ListedItem>();
@@ -572,26 +572,26 @@ void ApplyBulkInsertEntries()
572572
{
573573
OrderGroups();
574574
}
575-
};
575+
}
576576

577-
Action updateUIAction = () =>
577+
void UpdateUI()
578578
{
579579
// trigger CollectionChanged with NotifyCollectionChangedAction.Reset
580580
// once loading is completed so that UI can be updated
581581
FilesAndFolders.EndBulkOperation();
582582
UpdateEmptyTextType();
583583
DirectoryInfoUpdated?.Invoke(this, EventArgs.Empty);
584-
};
584+
}
585585

586586
if (CoreApplication.MainView.DispatcherQueue.HasThreadAccess)
587587
{
588-
await Task.Run(applyChangesAction);
589-
updateUIAction();
588+
await Task.Run(ApplyChanges);
589+
UpdateUI();
590590
}
591591
else
592592
{
593-
applyChangesAction();
594-
await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(updateUIAction);
593+
ApplyChanges();
594+
await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(UpdateUI);
595595
}
596596
}
597597
catch (Exception ex)
@@ -608,23 +608,23 @@ private Task OrderFilesAndFoldersAsync()
608608
return Task.CompletedTask;
609609
}
610610

611-
Action action = () =>
611+
void OrderEntries()
612612
{
613613
if (filesAndFolders.Count == 0)
614614
{
615615
return;
616616
}
617617

618618
filesAndFolders = SortingHelper.OrderFileList(filesAndFolders, folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection).ToList();
619-
};
619+
}
620620

621621
if (CoreApplication.MainView.DispatcherQueue.HasThreadAccess)
622622
{
623-
return Task.Run(action);
623+
return Task.Run(OrderEntries);
624624
}
625625
else
626626
{
627-
action();
627+
OrderEntries();
628628
return Task.CompletedTask;
629629
}
630630
}
@@ -1584,16 +1584,20 @@ await DialogDisplayHelper.ShowDialogAsync(
15841584
}
15851585
else
15861586
{
1587-
List<ListedItem> fileList = await Win32StorageEnumerator.ListEntries(path, returnformat, hFile, findData, Connection, cancellationToken, -1, intermediateAction: async (intermediateList) =>
1587+
await Task.Run(async () =>
15881588
{
1589-
filesAndFolders.AddRange(intermediateList);
1589+
List<ListedItem> fileList = await Win32StorageEnumerator.ListEntries(path, returnformat, hFile, findData, Connection, cancellationToken, -1, intermediateAction: async (intermediateList) =>
1590+
{
1591+
filesAndFolders.AddRange(intermediateList);
1592+
await OrderFilesAndFoldersAsync();
1593+
await ApplyFilesAndFoldersChangesAsync();
1594+
});
1595+
1596+
filesAndFolders.AddRange(fileList);
15901597
await OrderFilesAndFoldersAsync();
15911598
await ApplyFilesAndFoldersChangesAsync();
15921599
});
15931600

1594-
filesAndFolders.AddRange(fileList);
1595-
await OrderFilesAndFoldersAsync();
1596-
await ApplyFilesAndFoldersChangesAsync();
15971601
return 0;
15981602
}
15991603
}
@@ -1607,22 +1611,25 @@ private async Task EnumFromStorageFolderAsync(string path, ListedItem currentFol
16071611
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
16081612
string returnformat = Enum.Parse<TimeStyle>(localSettings.Values[Constants.LocalSettings.DateTimeFormat].ToString()) == TimeStyle.Application ? "D" : "g";
16091613

1610-
List<ListedItem> finalList = await UniversalStorageEnumerator.ListEntries(
1611-
rootFolder,
1612-
currentStorageFolder,
1613-
returnformat,
1614-
sourcePageType,
1615-
cancellationToken,
1616-
-1,
1617-
async (intermediateList) =>
1614+
await Task.Run(async () =>
16181615
{
1619-
filesAndFolders.AddRange(intermediateList);
1616+
List<ListedItem> finalList = await UniversalStorageEnumerator.ListEntries(
1617+
rootFolder,
1618+
currentStorageFolder,
1619+
returnformat,
1620+
sourcePageType,
1621+
cancellationToken,
1622+
-1,
1623+
async (intermediateList) =>
1624+
{
1625+
filesAndFolders.AddRange(intermediateList);
1626+
await OrderFilesAndFoldersAsync();
1627+
await ApplyFilesAndFoldersChangesAsync();
1628+
});
1629+
filesAndFolders.AddRange(finalList);
16201630
await OrderFilesAndFoldersAsync();
16211631
await ApplyFilesAndFoldersChangesAsync();
16221632
});
1623-
filesAndFolders.AddRange(finalList);
1624-
await OrderFilesAndFoldersAsync();
1625-
await ApplyFilesAndFoldersChangesAsync();
16261633

16271634
stopwatch.Stop();
16281635
Debug.WriteLine($"Enumerating items in {path} (device) completed in {stopwatch.ElapsedMilliseconds} milliseconds.\n");

0 commit comments

Comments
 (0)