2017-08-03 1 views
1

Ich habe eine kleine WPF-Anwendung, die in erster Linie nach MVVM-Muster geschrieben wird. Der Punkt des Programms besteht darin, die Zeilen einer Textdatei zu lesen, die Daten daraus zu analysieren, diese Daten in eine Liste von Objekten zu schreiben und dann die Daten in diese Objekte in eine spezifisch formatierte .CSV-Datei zu schreiben.Wie Implementieren von BackgroundWorker in WPF mit MVVM/ICommand-Muster

Obwohl ich die BackgroundWorker-Klasse selbst auf die gleiche Weise implementiert habe, die ich immer mit anderen Anwendungen habe, rufe ich diesmal die RunWorkAsync() -Methode innerhalb der Execute() -Methode meines ICommand auf. Während die endgültige Ausgabe korrekt ist und die Anwendung tatsächlich das gewünschte Ergebnis liefert, stürzt die UI STILL ab, während der BackgroundWorker ausgeführt wird.

Ich habe meine BackgroundWorker-Mitglieder und die gesamte Logik in einer Klasse namens "ReaderWriter" mit einem Konstruktor, der mein ViewModel als Parameter verwendet, eingepackt.

Durch den Aufruf der "StartProcess" -Methode meiner ReaderWriter-Instanz wird der RunWorkerAsync() des BackgroundWorker aufgerufen - hier hatte ich gehofft, dass er einen anderen Thread übernimmt und meinen lang andauernden Prozess zum Lesen der Quelldatei ausführt Analysieren der Daten und Schreiben der neuen Datei; während ReportProgress() regelmäßig aktualisiert wird, um die ProgressBar zu aktualisieren. Hier

ist der Code für meine Readerwriter Klasse:

class ReaderWriter 
{ 
    private LogDataViewModel vm { get; set; } 
    private BackgroundWorker bw { get; set; } 

    public ReaderWriter(LogDataViewModel viewModel) 
    { 
     vm = viewModel; 
    } 

    public void StartProcess() 
    { 
     bw = new BackgroundWorker(); 

     bw.WorkerReportsProgress = true; 
     bw.WorkerSupportsCancellation = true; 
     bw.DoWork += new DoWorkEventHandler(ReadFromSource); 
     bw.ProgressChanged += new ProgressChangedEventHandler(UpdateProgress_Read); 
     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Completed_Read); 

     bw.RunWorkerAsync(); 
    } 

    private void ReadFromSource(object sender, DoWorkEventArgs e) 
    { 
     double records = 0; 
     string[] lines = File.ReadAllLines(vm.SourcePath); 
     int lineCount = lines.Length; 
     double currentLine = 0; 

     bw.ReportProgress(0, lineCount); 

     foreach (var line in lines) 
     { 
      if (line.Length > 0) 
      { 
       string syntax = line.Substring(17, 6); 

       switch (syntax) 
       { 
        case "$WIMDA": 
         string[] segments = line.Replace(": <- ", ",").Split(','); 
         vm.LineItems.Add(new LineItem() 
         { 
          Time = segments[0], 
          HgPressure = segments[2], 
          BarPressure = segments[4], 
          AirTemp = segments[6], 
          RelHumidity = segments[10], 
          TrueWindDir = segments[14], 
          KnotsWindSpeed = segments[18], 
          MpsWindSpeed = segments[20] 
         }); 
         break; 

        case "$GPGGA": 
         break; 

        default: 
         break; 
       } 
      } 
      currentLine++; 
      bw.ReportProgress(1, currentLine); 
     } 
     using (StreamWriter writer = new StreamWriter(vm.OutputPath)) 
     { 
      writer.WriteLine($"Time,Pressure(Bar),Pressure(Hg),AirTemp({((vm.ConvertTempSetting) ? "F" : "C")}),RelativeHumidity,TrueWindDirection,WindSpeed(Knots),WindSpeed(M/s)"); 
      foreach (var lineItem in vm.LineItems) 
      { 
       writer.WriteLine($"{lineItem.Time},{lineItem.BarPressure},{lineItem.HgPressure},{((vm.ConvertTempSetting) ? Converters.ConvertFromCelcius(Convert.ToDouble(lineItem.AirTemp)).ToString() : lineItem.AirTemp)},{lineItem.RelHumidity},{lineItem.TrueWindDir},{lineItem.KnotsWindSpeed},{lineItem.MpsWindSpeed}"); 
       records++; 
      } 
     } 
     e.Result = records; 
    } 

    private void UpdateProgress_Read(object sender, ProgressChangedEventArgs e) 
    { 
     vm.IncrementProgress(); 
     switch (Type.GetTypeCode(e.UserState.GetType())) 
     { 
      case TypeCode.Double: 
       vm.IncrementProgress(); 
       break; 

      case TypeCode.String: 
       break; 

      case TypeCode.Int32: 
       vm.AppendStatus(DateTime.Now, $"{(int)e.UserState} lines parsed from log file"); 
       break; 

      default: 
       break; 
     } 
     if (vm.IsFirst) 
     { 
      vm.ProgressIsVisible = true; 
      vm.IncrementProgress(); 
      vm.SetMaximum((int)e.UserState); 
      vm.IsFirst = false; 
     } 
    } 
    private void Completed_Read(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (e.Cancelled) 
     { 
      vm.AppendStatus(DateTime.Now, $"Conversion was cancelled by user"); 
     } 
     else 
     { 
      vm.AppendStatus(DateTime.Now, $"{(double)e.Result} records written to {vm.OutputPath}"); 
     } 
     vm.LineItems.Clear(); 
    } 
} 

Und für meine Viewmodel:

public class LogDataViewModel : LogDataModel 
{ 
    #region Commands 
    public BeginProcessCommand BeginCommand { get; set; } 
    public SelectOutputPathCommand OutputCommand { get; set; } 
    public SelectSourceCommand SourceCommand { get; set; } 
    public ResetCommand ResetCommand { get; set; } 
    #endregion 

    public bool IsFirst { get; set; } 

    public LogDataViewModel() 
    { 
     BeginCommand = new BeginProcessCommand(this); 
     OutputCommand = new SelectOutputPathCommand(this); 
     SourceCommand = new SelectSourceCommand(this); 
     ResetCommand = new ResetCommand(this); 

     PrepareViewModel(); 
    } 


    private void PrepareViewModel() 
    { 
     ProgressValue = 0; 
     ProgressMaximum = 0; 
     ProgressIsVisible = false; 
     IsFirst = true; 

     OutputPath = Properties.Settings.Default.RememberedSavePath; 
     if (LineItems == null) LineItems = new List<LineItem>(); 
     if (StatusActions == null) StatusActions = new ObservableCollection<StatusAction>(); 
     AppendStatus(DateTime.Now, "Initialized Program"); 
    } 
} 

Und schließlich ist hier der Befehl:

public class BeginProcessCommand : ICommand 
{ 
    LogDataViewModel vm; 

    public BeginProcessCommand(LogDataViewModel viewModel) 
    { 
     vm = viewModel; 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public bool CanExecute(object parameter) 
    { 
     bool result = true; 

     if (!File.Exists(vm.SourcePath)) 
      result = false; 
     try 
     { 
      if (!Directory.Exists(Path.GetDirectoryName(vm.SourcePath))) 
       result = false; 
     } 
     catch 
     { 
      result = false; 
     } 
     return result; 
    } 

    public void Execute(object parameter) 
    { 
     ReaderWriter rw = new ReaderWriter(vm); 
     rw.StartProcess(); 
    } 
} 

Jede Hilfe bei Dieser Punkt wird sehr geschätzt, da ich seit einiger Zeit damit zu kämpfen habe und versucht habe, nach Lösungen zu suchen gib keine Hilfe für meine besondere Situation. Dies scheint ein ziemlich einzigartiges Szenario zu sein, aber ich hoffe, dass wir es zum Laufen bringen können.

Vielen Dank!

+0

Es ist, weil Sie die Benutzeroberfläche mit Updates pummeling sind. Versuchen Sie nur alle 100 Zeilen einen Fortschritt zu melden. Wenn Sie eine Menge Zeilen haben und die Schleife ziemlich schnell dreht, erhöhen Sie diese Zahl. – Will

+0

Danke, Will. Ich habe getan, was du vorgeschlagen hast und es hat wie ein Zauber funktioniert! –

Antwort

2

Sie verwenden ReportProgress falsch und viel zu oft (in jeder Zeile in der Datei). Es wird gehämmert werden und jeder Anruf verursacht eine Art Update in Ihrer UI und schließt es ab.

ReportProgress ist wahrscheinlich am einfachsten zu verwenden, indem Sie einen Prozentsatz an es übergeben. Ich bin nicht wirklich sicher, was Sie in UpdateProgress_Read mit dem Schalter tun. Es wäre am besten, nur zu aktualisieren, wenn Sie ein 100. Ihrer gesamten Zeilen übergeben.

stellen Sie Ihren progressBar maximal 100

ProgressMaximum = 100; 

berechnen 1% Ihrer gesamten Linien

var x = lineCount/100; 
var y = 0; 

und nur Fortschritte berichten, wie Sie jeweils 1%

currentLine++; 
if((currentLine % x) == 0) 
{ 
    y++; 
    bw.ReportProgress(y); 
} 

und Veränderung passieren UpdateProgress_Read, so dass es nur

inkrementiert
private void UpdateProgress_Read(object sender, ProgressChangedEventArgs e) 
{ 
    vm.IncrementProgress(); 
} 

Sie müssen bessere Variablennamen dann x und y kommen! und auch herauszufinden, was zu tun ist, wenn Sie weniger als 100 Zeilen in der Datei haben.

+0

Vielen Dank für Ihre ausführliche Antwort, ajg! Ihre ausführliche Erklärung hat mir geholfen, besser zu verstehen, wie ReportProgress() verwendet werden soll. Ich habe ein wenig Code in meiner Foreachiteration geändert und es funktioniert jetzt perfekt. Ich bin froh, dass es ein viel einfacheres Problem war, das ich ursprünglich vermutet hatte! =) –

Verwandte Themen