2017-08-03 9 views
1

主に以下のMVVMパターンで記述された小さなWPFアプリケーションがあります。プログラムのポイントは、テキストファイルの行を読み取り、そのデータを解析し、そのデータをオブジェクトのリストに書き込み、それらのオブジェクトのデータを特別な形式の.CSVファイルに書き込むことです。MVVM/ICommandパターンでWPFでBackgroundWorkerを正しく実装する方法

私はいつも他のアプリケーションと同じようにBackgroundWorkerクラス自体を実装していますが、今回は私のICommandのExecute()メソッドからRunWorkAsync()メソッドを呼び出しています。最終的な出力は正しいものの、アプリケーションが実際に望みの結果を出力している間、UIはBackgroundWorkerの実行中にロックされます。

BackgroundWorkerのメンバーと "ReaderWriter"という名前のクラス内のすべてのロジックを、ViewModelをパラメータとして受け取るコンストラクタでラップしました。私のリーダライタインスタンスの「のStartProcess」メソッドを呼び出すことによって

、BackgroundWorkerののRunWorkerAsync()と呼ばれている - 私は期待していた、それは別のスレッドに引き継ぐとソースファイルを読み取るので、私の長時間実行プロセスを行うだろう場所ですデータの解析、および新しいファイルの書き込みを行います。すべて定期的にReportProgress()を実行してProgressBarを更新します。

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(); 
    } 
} 

そして、私の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"); 
    } 
} 

そして最後に、ここでのコマンドは次のとおりです。

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(); 
    } 
} 

すべてのヘルプでこの点は非常に高く評価されています。私はこれとしばらくの間これを苦労し、解決策を研究しようとしています私の特定の状況のた​​めの助けを得ない。これは非常にユニークなシナリオのようですが、私はそれを機能させることができると期待しています。

ありがとうございました!

+0

これは、あなたがアップデートでUIを撃退しているからです。 100行ごとに進捗状況を報告してください。たくさんの行があり、ループが非常に速くスピンするなら、その数を上げてください。 – Will

+0

ありがとう、ウィル私はあなたが提案したことをして、それは魅力のように働いた! –

答えて

2

ReportProgressを間違って頻繁に使用しています(ファイル内のすべての行で)。それは叩かれているだろうし、すべての呼び出しはあなたのUIでいくつかの並べ替えを引き起こしているので、それをロックしています。

ReportProgressは、パーセンテージを渡すことでおそらく最も使いやすいでしょう。私は本当にスイッチでUpdateProgress_Readで何をしているのかは分かりません。総ライン数の100分の1を渡すだけで更新するのが最善です。

は100

ProgressMaximum = 100; 

計算あなたの総ラインの1%にあなたのプログレスバーの最大値を設定し

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

、あなたはそれぞれ1%

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

や変更を通過するだけで進捗状況を報告UpdateProgress_Readを増分するだけで

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

あなたはxとyのより良い変数名を考え出す必要があります!また、ファイル内に100行未満の行がある場合は、何をすべきかを検討します。

+0

詳細な回答、ajgありがとう!あなたの詳細な説明は、ReportProgress()をどのように使用するのかを理解するのに役立ちました。私はforeach反復で少しのコードを修正しましたが、今は完全に動作しています。もともと私が疑っていたことははるかに簡単な問題だったことをうれしく思っています! =) –

関連する問題