2017-01-08 2 views
0

Ich möchte ein Ordner-Cleaner-Programm erstellen. Es wird erwartet, dass gelöschte Dateien in Echtzeit an eine TextBox Steuerung gemeldet werden. So verwende ich await Task.Run(() => CleanFolder(folderPath, progress)) Funktion in meinem Button Click Event. Aber die Benutzeroberfläche wurde beim Ausführen blockiert. Nach einer Weile, wenn die Methode CheanFolder() ausgeführt wird, werden alle gelöschten Dateien gleichzeitig angezeigt.UI bei Verwendung blockiert warten auf eine zeitraubende Aufgabe

namespace FolderCleaner 
{ 
    public partial class MainWindow : Window 
    { 
     string folderPath; 
     string matchPattern; 

     private void ButtonOpen_Click(object sender, RoutedEventArgs e) 
     { 
      FolderBrowserDialog fbd = new FolderBrowserDialog() { Description = "Select a folder" }; 
      if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK) 
      { 
       folderPath = fbd.SelectedPath; 
       textBoxPath.Text = folderPath; 
       buttonClean.IsEnabled = true; 
       textBoxList.Text = "Folder path: " + folderPath + "\n"; 
      } 
     } 

     private async void ButtonClean_Click(object sender, RoutedEventArgs e) 
     { 
      matchPattern = textBoxPattern.Text; 
      buttonOpen.IsEnabled = false; 
      buttonClean.IsEnabled = false; 
      Progress<string> progress = new Progress<string>(msg => 
      { 
       textBoxList.AppendText("File deleted: " + msg + "\n"); 
       textBoxList.CaretIndex = textBoxList.Text.Length; 
       textBoxList.ScrollToEnd(); 
      }); 

      try 
      { 
       await Task.Run(() => CleanFolder(folderPath, progress)); 

       textBoxList.AppendText("Mission complete!"); 
       textBoxList.CaretIndex = textBoxList.Text.Length; 
       textBoxList.ScrollToEnd(); 
      } 
      catch 
      { 
       System.Windows.MessageBox.Show("Error!"); 
      } 
      finally 
      { 
       buttonOpen.IsEnabled = true; 
      } 
     } 

     private void CleanFolder(string path, IProgress<string> progress) 
     { 
      var filePaths = Directory.EnumerateFiles(path, "*.*", System.IO.SearchOption.AllDirectories); 
      foreach (var filePath in filePaths) 
      { 
       var matchResult = Regex.Match(filePath, matchPattern); 
       if (matchResult.Success) 
       { 
        File.Delete(filePath); 
        progress.Report(filePath); 
       } 
      } 
     } 
    } 
} 
+0

Das Anzeigen des gesamten MainWindow-Codes ist hier nicht erforderlich. Sie können die irrelevanten Codeteile wie den Konstruktor oder den ButtonOpen_Click-Handler entfernen, um Ihre Frage leichter verständlich zu machen. – Clemens

+0

Ich denke, Sie sollten .Invoke() verwenden, um Ihre TextBox zu aktualisieren – McNets

Antwort

2

GUI kann nicht von einem anderen Thread aus gesteuert werden.

Aber ich denke, das eigentliche Problem ist, dass die Verkettung von String und Ausgabe an eine TextBox ist eine sehr ineffiziente Operation.

In Ihrem Fall ist es besser, den Fortschritt des Entfernens in einer einzelnen Zeile anzuzeigen oder den Fortschrittsbalken zu verwenden.

Hier ist meine Lösung des Problems ist (`ve geändert 2 Methoden):

private async void ButtonClean_Click(object sender, RoutedEventArgs e) 
    { 
     matchPattern = textBoxPattern.Text; 
     buttonOpen.IsEnabled = false; 
     buttonClean.IsEnabled = false; 

     await Task.Run(() => CleanFolder(folderPath)); 

     textBoxList.Text += "Mission complete!"; 
     buttonOpen.IsEnabled = true; 
    } 

    private void CleanFolder(string path) 
    { 
     var filePaths = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories); 
     foreach (var filePath in filePaths) 
     { 
      var matchResult = Regex.Match(filePath, matchPattern); 
      if (matchResult.Success) 
      { 
       File.Delete(filePath); 
       System.Windows.Application.Current.Dispatcher.Invoke(delegate 
       { 
        // this working fast 
        textBoxList.Text = "File deleted: " + filePath + "\n"; 
        // this working slow and slower over time 
        //textBoxList.Text += "File deleted: " + filePath + "\n"; 
        textBoxList.ScrollToEnd(); 
       }); 
      } 
     } 
    } 

Ich hoffe, das hilft.

0

Danke für alle. Danke an das Buch C# 6.0 in aller Kürze

Ich habe die Lösung herausgefunden und habe ein besseres Verständnis von async/erwarten.

Zunächst wird Dispatcher.Invoke nicht empfohlen zu verwenden, da .Net Framework 4.5, task-basierte asynchrony das dominante Muster geworden ist (mit async/awit).

Zweitens gibt es einige Prinzipien der Verwendung von Asynchron/erwarten:

  • Der Ausdruck nach await

  • Wenn Sie async Modifikator auf ein Verfahren verwenden ein Task oder Task<TResult> Objekt sein muss, dann die Methode don t need to return a Aufgabe method manually. The compile will wrap the method as a Task` Objekt. Wenn Sie eine Methode wie async Task Foo() verwenden, müssen Sie ein Schlüsselwort await darin verwenden.

  • Wenn es nichts zu erwarten, dann entfernen Sie den async Modifikator, gibt ein Task Objekt durch return Task.Run(() => { Do Something }); verwenden. Jetzt können Sie await Foo() in der Methode verwenden, die Foo() aufruft.

  • Task Foo() kann UI nicht bedienen, aber async Task Foo() kann.

Verwandte Themen