2012-12-31 16 views
5

私は、WinForms C#アプリケーションで別スレッドを使用して、ProgressBar(マーキー)を制御するバックグラウンドワーカーを開始しようとしています。問題は、私がバーを目に見えるようにしようとすると、何もしません。そして、私はInvokeの多くのフォームを試しましたが、助けてくれないようです。別のスレッド内からUI要素を操作する

以下のメソッドprogressBarCycleが別のスレッドから呼び出されます。

BackgroundWorker backgroundWorker = new BackgroundWorker(); 

    public void progressBarCycle(int duration) 
    { 
     backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); 
     backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); 
     backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted); 
     backgroundWorker.WorkerReportsProgress = true; 
     backgroundWorker.WorkerSupportsCancellation = true; 
     backgroundWorker.RunWorkerAsync(duration); 
    } 

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 

     worker.ReportProgress(0); 

     DateTime end = DateTime.Now.AddMilliseconds((int)e.Argument); 
     while (DateTime.Now <= end) 
     { 
      System.Threading.Thread.Sleep(1000); 
     } 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (!this.IsHandleCreated) 
      this.CreateHandle(); 
     statusStrip1.Invoke((MethodInvoker)delegate 
     { 
      progressBar1.Visible = false; 
     }); 
     // if (!this.IsHandleCreated) 
     // { 
     //  this.CreateHandle(); 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false)); 
     //  else progressBar1.Visible = false; 
     // } 
     // else 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false)); 
     //  else progressBar1.Visible = false; 
    } 

    private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     if (!this.IsHandleCreated) 
      this.CreateHandle(); 
     statusStrip1.Invoke((MethodInvoker)delegate 
     { 
      progressBar1.Visible = true; 
     }); 
     // if (!this.IsHandleCreated) 
     // { 
     //  this.CreateHandle(); 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true)); 
     //  else progressBar1.Visible = true; 
     // } 
     // else 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true)); 
     //  else progressBar1.Visible = true; 
    } 

私はここで何かが分かりませんか?コメントセクションは、私が試した他のものです。

答えて

6

ProgressChangedは、です。すでにがUIスレッドで生成されています(sync-context経由)。あなたのProgressChangedはそれをする必要はありませんInvoke - これはUIを直接操作することができます(対照的に、DoWorkは絶対にではありません。です)。おそらく実際の問題は、あなたが何もしないことですworker.ReportProgress(...)の中にループ - それは開始時に一度だけ起こります。 UIスレッドから

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 
static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     using (var worker = new BackgroundWorker { 
      WorkerReportsProgress = true }) 
     using (var progBar = new ProgressBar { 
      Visible = false, Step = 1, Maximum = 100, 
      Dock = DockStyle.Bottom }) 
     using (var btn = new Button { Dock = DockStyle.Top, Text = "Start" }) 
     using (var form = new Form { Controls = { btn, progBar } }) 
     { 
      worker.ProgressChanged += (s,a) => { 
       progBar.Visible = true; 
       progBar.Value = a.ProgressPercentage; 
      }; 
      worker.RunWorkerCompleted += delegate 
      { 
       progBar.Visible = false; 
      }; 
      worker.DoWork += delegate 
      { 
       for (int i = 0; i < 100; i++) 
       { 
        worker.ReportProgress(i); 
        Thread.Sleep(100); 
       } 
      }; 
      btn.Click += delegate 
      { 
       worker.RunWorkerAsync(); 
      }; 
      Application.Run(form); 
     } 
    } 
} 
+0

純粋に私の問題を解決するかどうかをProgressChangedとRunWorkerCompletedに入れました。 DoWorkにすべてのコードが含まれていたら、まったく同じことが起こります。Visibleの状態は、呼び出しの有無にかかわらず、決して変更されません。 - ああ、それはおそらく、これはマーキーであることに言及する価値があるので、私は進捗報告を使用するつもりはありません。 – UncleDave

+0

@UncleDaveこれは、プログレスバーが表示されない別のコントロール(パネルかもしれない)の中にあるように聞こえる。 –

+0

プログレスバーはこのコードを実行するフォーム上にあります。これはアプリケーションの開始フォームです。 – UncleDave

2
  1. 実行progressBarCycle

    はここで完全な例です。 RunWorkerAsync 新しいスレッドを作成します。

  2. backgroundWorker_ProgressChangedでは、単に progressBar1.Visible = true;を呼び出します。 Invokeの必要はありません。
  3. さらに、progressBar1.Refresh();を追加してください。
2

もう1つの可能性は、進行状況バーがUIスレッドで実行されていることです。プログレスバーが表示され、新しい進捗量を表示するために再描画されるためには、UIスレッドはメインアプリケーションループ内のウィンドウメッセージを自由に実行している必要があります。

バックグラウンドワーカースレッドを開始した後、UIスレッドがビジー状態の待機ループに入り、完了するまで待っていたり、他の作業が多かったりしても、Windowsメッセージと処理されません進行状況バーが「応答しない」と表示されます。この更新が引き続き行われるように(つまり、イベントハンドラから戻り、UIが正常に実行されるように)、UIスレッドを解放する必要があります。

この危険性は、UIがアクティブな場合、ユーザーは引き続きユーザーとやりとりすることができるということです。したがって、バックグラウンドワーカーがアクティブなときに認識し、状況を適切に処理するためのUIを記述する必要があります(問題は、バックグラウンドワーカーがすでに実行中にバックグラウンドワーカーを再起動できるようにすること、ワーカーが作業中に情報を表示しようとすることスレッドが忙しく更新しているか、バックグラウンドワーカーがビジー状態である間に新しい文書をロードするか終了するかを決定するなど)。この2つの主な解決策は、バックグラウンド作業が実行されている間に開始される危険なものをすべて防御する保護シールドにUIのすべてのビットをラップすることです(これは、このようにラップするコントロールがたくさんある場合、バグを解決する間違いやUIを「保護されない」状態にするのは簡単ですが、受信ウィンドウメッセージ(WM_KEYPRESSなど)を抑止することで「危険な」ユーザー操作(クリックやキー押し)をすべて停止するIMessageFilterを追加しますバックグラウンド処理がアクティブです。

関連する問題