When await ChangeDirectory(path) started, the main thread stops and the UI does not respond until the method runs.

Question 1: why is this happening?

Question 2: how to make the UI update with each new addition to the ObservableCollection?

 public ObservableCollection<FoldersVm> Folders { get; } public async void MouseDoubleClick(object sender, MouseButtonEventArgs e) { var listBox = sender as ListBox; var foldersVm = listBox?.SelectedItem as FolderVm; if (foldersVm != null) { var path = foldersVm.FullPath; if (File.GetAttributes(path) == FileAttributes.Directory) { await ChangeDirectory(path); _previousPath = path; } } } private Task ChangeDirectory(string path) { return Task.Run(() => { Application.Current.Dispatcher.BeginInvoke((Action) delegate() { Folders.Clear(); foreach (var dir in Directory.EnumerateDirectories(path)) { var attr = File.GetAttributes(dir); if ((attr & FileAttributes.Hidden) != 0 || (attr & FileAttributes.System) != 0) continue; Folders.Add(new FolderVm(Path.GetFileName(dir), ImagesHelper.GetFolderImage(dir), dir)); } foreach (string file in Directory.EnumerateFiles(path)) { if (Path.GetExtension(file) == ".jpg") Folders.Add(new FolderVm(Path.GetFileName(file), ImagesHelper.GetFrozenImage(file, 200), file)); } }); }); } 

ImageHelper.GetFolderImage

 public static ImageSource GetFolderImage(string path) { var sf = ShellObject.FromParsingName(path) as ShellFolder; if (sf.Thumbnail != null) return sf.Thumbnail.BitmapSource.Clone() as ImageSource; return null; } 

Update for VladD

The problem was in the GetFolderImage method, solved it like this:

 public static ImageSource GetFolderImage(string path) { var sf = ShellObject.FromParsingName(path) as ShellFolder; if (sf.Thumbnail != null) { var a = sf.Thumbnail.BitmapSource; a.Freeze(); return a; } return null; } 
  • 3
    I remember this code! Where do you all take it? - Pavel Mayorov
  • 2
    @PavelMayorov, you are confusing something, I wrote all this code myself! - Lightness

1 answer 1

Because in the code

 Task.Run(() => { Application.Current.Dispatcher.BeginInvoke((Action) delegate() { 

the second third line cancels the first. The first line launches the lambda in the background thread. The second Third line of lambda (asynchronously) starts the delegate in the UI thread, and blocks it for the duration of the entire delegate.

You can try to asynchronize the ChangeDirectory function. You will need a model DTO object to transfer data, since VM objects have thread affinity (that is, they must be created in the main thread).

 class FolderEntryModel // это ваш DTO-объект { public string FileName, Image, DirectoryName; } static void EnumerateDirectoriesWithProgress( string path, IProgress<FolderEntryModel> progress, CancellationToken ct) { foreach (var dir in Directory.EnumerateDirectories(path)) { var attr = File.GetAttributes(dir); if ((attr & FileAttributes.Hidden) != 0 || (attr & FileAttributes.System) != 0) continue; if (ct.IsCancellationRequested) break; progress.Report(new FolderEntryModel() { FileName = Path.GetFileName(dir), Image = ImagesHelper.GetFolderImage(dir), DirectoryName = dir }); } } async Task ChangeDirectory(string path, CancellationToken ct) { Folders.Clear(); var progress = new Progress<FolderEntryModel>(m => { if (!ct.IsCancellationRequested) Folders.Add(new FolderVM(m.FileName, m.Image, m.DirectoryName)); }); await Task.Run(() => EnumerateDirectoriesWithProgress(path, progress, ct)); } 

We still need to add a token to cancel as a field:

 CancellationTokenSource CurrentDirectoryReadCancellation; 

Now, in order to start a new scan, and cancel the old one, do this:

 async Task CancelDirectoryChangeAndStartNew(string path) { CurrentDirectoryReadCancellation?.Cancel(); var cts = CurrentDirectoryReadCancellation = new CancellationTokenSource(); await ChangeDirectory(RootFolder, cts.Token); // если никто не начался после нас, подчистим if (CurrentDirectoryReadCancellation == cts) CurrentDirectoryReadCancellation = null; } 

Progress<T> allows you to transfer an event to a UI stream, so we have an easy version as producer / consumer.


Update: added cancellation option to code.

  • if you find fault, then on the second line - bracket :) - Grundy
  • @Grundy: Fixed :) - VladD
  • gut :-)) accepted :-D - Grundy
  • @VladD, in the progress.Report line, throws a Must create DependencySource on same Thread as the DependencyObject. exception Must create DependencySource on same Thread as the DependencyObject. - Lightness
  • @Lightness: Then you do something wrong in my code. Describe how your code differs from mine. - VladD