@@ -30,6 +30,7 @@ public sealed class ItemViewModel : ObservableObject, IDisposable
30
30
{
31
31
private readonly SemaphoreSlim enumFolderSemaphore ;
32
32
private readonly SemaphoreSlim getFileOrFolderSemaphore ;
33
+ private readonly SemaphoreSlim bulkOperationSemaphore ;
33
34
private readonly ConcurrentQueue < ( uint Action , string FileName ) > operationQueue ;
34
35
private readonly ConcurrentQueue < uint > gitChangesQueue ;
35
36
private readonly ConcurrentDictionary < string , bool > itemLoadQueue ;
@@ -484,6 +485,7 @@ public ItemViewModel(LayoutPreferencesManager folderSettingsViewModel)
484
485
gitChangedEvent = new AsyncManualResetEvent ( ) ;
485
486
enumFolderSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
486
487
getFileOrFolderSemaphore = new SemaphoreSlim ( 50 ) ;
488
+ bulkOperationSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
487
489
dispatcherQueue = DispatcherQueue . GetForCurrentThread ( ) ;
488
490
489
491
UserSettingsService . OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent ;
@@ -687,41 +689,49 @@ void ClearDisplay()
687
689
// Note that both DataGrid and GridView don't support multi-items changes notification, so here
688
690
// we have to call BeginBulkOperation to suppress CollectionChanged and call EndBulkOperation
689
691
// in the end to fire a CollectionChanged event with NotifyCollectionChangedAction.Reset
690
- FilesAndFolders . BeginBulkOperation ( ) ;
691
-
692
- // After calling BeginBulkOperation, ObservableCollection.CollectionChanged is suppressed
693
- // so modifies to FilesAndFolders won't trigger UI updates, hence below operations can be
694
- // run safely without needs of dispatching to UI thread
695
- void ApplyChanges ( )
692
+ await bulkOperationSemaphore . WaitAsync ( addFilesCTS . Token ) ;
693
+ try
696
694
{
697
- if ( addFilesCTS . IsCancellationRequested )
698
- return ;
695
+ FilesAndFolders . BeginBulkOperation ( ) ;
699
696
700
- FilesAndFolders . Clear ( ) ;
701
- FilesAndFolders . AddRange ( filesAndFoldersLocal ) ;
697
+ // After calling BeginBulkOperation, ObservableCollection.CollectionChanged is suppressed
698
+ // so modifies to FilesAndFolders won't trigger UI updates, hence below operations can be
699
+ // run safely without needs of dispatching to UI thread
700
+ void ApplyChanges ( )
701
+ {
702
+ if ( addFilesCTS . IsCancellationRequested )
703
+ return ;
702
704
703
- if ( folderSettings . DirectoryGroupOption != GroupOption . None )
704
- OrderGroups ( ) ;
705
- }
705
+ FilesAndFolders . Clear ( ) ;
706
+ FilesAndFolders . AddRange ( filesAndFoldersLocal ) ;
706
707
707
- void UpdateUI ( )
708
- {
709
- // Trigger CollectionChanged with NotifyCollectionChangedAction.Reset
710
- // once loading is completed so that UI can be updated
711
- FilesAndFolders . EndBulkOperation ( ) ;
712
- UpdateEmptyTextType ( ) ;
713
- DirectoryInfoUpdated ? . Invoke ( this , EventArgs . Empty ) ;
714
- }
708
+ if ( folderSettings . DirectoryGroupOption != GroupOption . None )
709
+ OrderGroups ( ) ;
710
+ }
715
711
716
- if ( NativeWinApiHelper . IsHasThreadAccessPropertyPresent && dispatcherQueue . HasThreadAccess )
717
- {
718
- await Task . Run ( ApplyChanges ) ;
719
- UpdateUI ( ) ;
712
+ void UpdateUI ( )
713
+ {
714
+ // Trigger CollectionChanged with NotifyCollectionChangedAction.Reset
715
+ // once loading is completed so that UI can be updated
716
+ FilesAndFolders . EndBulkOperation ( ) ;
717
+ UpdateEmptyTextType ( ) ;
718
+ DirectoryInfoUpdated ? . Invoke ( this , EventArgs . Empty ) ;
719
+ }
720
+
721
+ if ( NativeWinApiHelper . IsHasThreadAccessPropertyPresent && dispatcherQueue . HasThreadAccess )
722
+ {
723
+ await Task . Run ( ApplyChanges ) ;
724
+ UpdateUI ( ) ;
725
+ }
726
+ else
727
+ {
728
+ ApplyChanges ( ) ;
729
+ await dispatcherQueue . EnqueueOrInvokeAsync ( UpdateUI ) ;
730
+ }
720
731
}
721
- else
732
+ finally
722
733
{
723
- ApplyChanges ( ) ;
724
- await dispatcherQueue . EnqueueOrInvokeAsync ( UpdateUI ) ;
734
+ bulkOperationSemaphore . Release ( ) ;
725
735
}
726
736
}
727
737
catch ( Exception ex )
@@ -820,30 +830,38 @@ public async Task GroupOptionsUpdatedAsync(CancellationToken token)
820
830
821
831
try
822
832
{
823
- FilesAndFolders . BeginBulkOperation ( ) ;
824
- UpdateGroupOptions ( ) ;
825
-
826
- if ( FilesAndFolders . IsGrouped )
833
+ await bulkOperationSemaphore . WaitAsync ( token ) ;
834
+ try
827
835
{
828
- await Task . Run ( ( ) =>
836
+ FilesAndFolders . BeginBulkOperation ( ) ;
837
+ UpdateGroupOptions ( ) ;
838
+
839
+ if ( FilesAndFolders . IsGrouped )
829
840
{
830
- FilesAndFolders . ResetGroups ( token ) ;
831
- if ( token . IsCancellationRequested )
832
- return ;
841
+ await Task . Run ( ( ) =>
842
+ {
843
+ FilesAndFolders . ResetGroups ( token ) ;
844
+ if ( token . IsCancellationRequested )
845
+ return ;
833
846
834
- OrderGroups ( ) ;
835
- } ) ;
847
+ OrderGroups ( ) ;
848
+ } ) ;
849
+ }
850
+ else
851
+ {
852
+ await OrderFilesAndFoldersAsync ( ) ;
853
+ }
854
+
855
+ if ( token . IsCancellationRequested )
856
+ return ;
857
+
858
+ await dispatcherQueue . EnqueueOrInvokeAsync (
859
+ FilesAndFolders . EndBulkOperation ) ;
836
860
}
837
- else
861
+ finally
838
862
{
839
- await OrderFilesAndFoldersAsync ( ) ;
863
+ bulkOperationSemaphore . Release ( ) ;
840
864
}
841
-
842
- if ( token . IsCancellationRequested )
843
- return ;
844
-
845
- await dispatcherQueue . EnqueueOrInvokeAsync (
846
- FilesAndFolders . EndBulkOperation ) ;
847
865
}
848
866
catch ( Exception ex )
849
867
{
0 commit comments