4

我是C#和麪向對象編程的新手。我一直在試圖在我的GUI中實現「取消」按鈕,以便用戶可以在進程中停止它。將參數傳遞到backgroundWorker(用作取消按鈕)

我讀到這個問題:How to implement a Stop/Cancel button?並確定backgroundWorker應該是一個很好的選擇,但給出的例子並沒有解釋如何將參數傳遞給backgroundWorker。

我的問題是,我不知道如何將參數傳遞到backgroundWorker,以便它會停止該過程;我只能讓backgroundWorker停下來。

我創建了下面的代碼,試圖瞭解這一點,在我的形式有兩個按鈕(buttonStart和buttonStop)和一個BackgroundWorker(backgroundWorkerStopCheck):

using System; 
using System.ComponentModel; 
using System.Windows.Forms; 
using System.Threading; 
using System.Timers; 

namespace TestBackgroundWorker 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     {   
      InitializeComponent(); 

      // Set the background worker to allow the user to stop the process. 
      backgroundWorkerStopCheck.WorkerSupportsCancellation = true; 
     } 

     private System.Timers.Timer myTimer; 

     private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e) 
     { 
      //If cancellation is pending, cancel work. 
      if (backgroundWorkerStopCheck.CancellationPending) 
      { 
       e.Cancel = true; 
       return; 
      } 
     } 

     private void buttonStart_Click(object sender, EventArgs e) 
     { 
      // Notify the backgroundWorker that the process is starting. 
      backgroundWorkerStopCheck.RunWorkerAsync(); 
      LaunchCode(); 
     } 

     private void buttonStop_Click(object sender, EventArgs e) 
     { 
      // Tell the backgroundWorker to stop process. 
      backgroundWorkerStopCheck.CancelAsync(); 
     } 

     private void LaunchCode() 
     { 
      buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing. 
      myTimer = new System.Timers.Timer(5000); // Waste five seconds. 
      myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed); 
      myTimer.Enabled = true; // Start the timer. 
     } 

     void myTimer_Elapsed(object sender, ElapsedEventArgs e) 
     { 
      buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled. 
     } 
    } 
} 

的代碼,如果一切正常,只想在用戶點擊「開始」之前,在重新啓用「開始」按鈕之前坐在那裏五秒鐘,或者如果用戶點擊「停止」,則快速重新激活「開始」按鈕。

有兩個問題與此代碼,我不知道如何處理:

1)「myTimer_Elapsed」方法會導致一個InvalidOperationException當嘗試啓用開始按鈕,因爲「跨線程操作無效「。我如何避免跨線程操作?

2)現在backgroundWorker沒有完成任何事情,因爲我不知道如何向它提供參數,這樣當它被取消時,它會停止計時器。

我很感謝任何幫助!

回答

4

首先,避免「跨線程操作無效」的問題是在控件上使用Invoke。您不能使用來自其他線程的控件。

關於第二個問題,我將以下面的方式實現它。這是具有取消支持的最低後臺工作者實施。

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 

namespace WindowsFormsApplication5 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 

      // Set the background worker to allow the user to stop the process. 
      backgroundWorkerStopCheck.WorkerSupportsCancellation = true; 
      backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork); 
     } 

     private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e) 
     { 
      try 
      { 
       for (int i = 0; i < 50; i++) 
       { 
        if (backgroundWorkerStopCheck.CancellationPending) 
        { 
         // user cancel request 
         e.Cancel = true; 
         return; 
        } 

        System.Threading.Thread.Sleep(100); 
       } 
      } 
      finally 
      { 
       InvokeEnableStartButton(); 
      } 
     } 

     private void buttonStart_Click(object sender, EventArgs e) 
     { 
      //disable start button before launch work 
      buttonStart.Enabled = false; 

      // start worker 
      backgroundWorkerStopCheck.RunWorkerAsync(); 
     } 

     private void buttonStop_Click(object sender, EventArgs e) 
     { 
      // Tell the backgroundWorker to stop process. 
      backgroundWorkerStopCheck.CancelAsync(); 
     } 

     private void InvokeEnableStartButton() 
     { 
      // this method is called from a thread, 
      // we need to Invoke to avoid "cross thread exception" 
      if (this.InvokeRequired) 
      { 
       this.Invoke(new EnableStartButtonDelegate(EnableStartButton)); 
      } 
      else 
      { 
       EnableStartButton(); 
      } 
     } 

     private void EnableStartButton() 
     { 
      buttonStart.Enabled = true; 
     } 
    } 

    internal delegate void EnableStartButtonDelegate(); 
} 

關於傳遞參數給工人,你可以通過在RunWorkerAsync()方法的對象,其在backgroundWorkerStopCheck_DoWork方法收訖:

... 
    backgroundWorkerStopCheck.RunWorkerAsync("hello"); 
    ... 

    private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e) 
    { 
     string argument = e.Argument as string; 
     // argument value is "hello" 
     ... 
    } 

希望它能幫助。

+0

非常感謝你,丹尼爾。這非常豐富! – Patrigon

3

嘗試這個例子,你會看到如何將數據傳入和傳出BackgroundWorker的:

public partial class Form1 : Form 
{ 
    BackgroundWorker bw = new BackgroundWorker(); 

    public Form1() 
    { 
     InitializeComponent(); 

     bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
     bw.WorkerSupportsCancellation = true; 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     btnStart.Enabled = false; 
     btnCancel.Enabled = true; 

     double[] data = new double[1000000]; 
     Random r = new Random(); 
     for (int i = 0; i < data.Length; i++) 
      data[i] = r.NextDouble(); 

     bw.RunWorkerAsync(data); 
    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     btnStart.Enabled = true; 
     btnCancel.Enabled = false; 

     if (!e.Cancelled) 
     { 
      double result = (double)e.Result; 
      MessageBox.Show(result.ToString()); 
     } 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     double[] data = (double[])e.Argument; 

     for (int j = 0; j < 200; j++) 
     { 
      double result = 0; 
      for (int i = 0; i < data.Length; i++) 
      { 
       if (bw.CancellationPending) 
       { 
        e.Cancel = true; 
        return; 
       } 
       result += data[i]; 
      } 
      e.Result = result; 
     } 
    } 

    private void btnCancel_Click(object sender, EventArgs e) 
    { 
     bw.CancelAsync(); 
     btnStart.Enabled = true; 
     btnCancel.Enabled = false; 
    } 
} 
+0

+1爲好的實現示例。我不確定在btnCancelClick處理程序中需要按鈕啓用/禁用語句。這些按鈕將在bw_runWorkerCompleted中啓用/禁用。如果語句在btn_Cancel_Click中被複制,如本代碼所示,那麼在背景工作者完成取消操作之前,您可能會面臨啓用「啓動」按鈕的風險,如果在後臺工作器中運行的操作需要幾秒鐘時間取消。 –