2010-07-21 52 views
13

我有一個C#窗體窗體應用程序,我扔在一起。這是相當簡單:\C#更新並使用backgroundworker進程追加文本框的值

輸入:

  • 文本字符串
  • 源文件夾路徑
  • 目標文件夾路徑
  • 整數通過在文本文件數

該應用程序的搜索輸入的文本字符串的源文件夾;如果它找到該字符串,則會將該文件和一個具有相同名稱的圖像文件複製到目標文件夾。它根據整數輸入無論如何都要這麼做。

所以我有一個按鈕,在按鈕單擊事件我打電話

ProcessImages(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString()); 

是:

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages) 
     {   
      int ImageCounter = 0; 
      int MaxImages = Convert.ToInt32(strNumImages); 

      DirectoryInfo di = new DirectoryInfo(SourceFolder); 

      foreach (FileInfo fi in di.GetFiles("*.txt")) 
      { 
       if (fi.OpenText().ReadToEnd().Contains(DID)) 
       { 
        //found one! 
        FileInfo fi2 = new FileInfo(fi.FullName.Replace(".txt", ".tif")); 
        if (fi2.Exists) 
        { 
         try 
         { 
          tbOutput.Text += "Copying " + fi2.FullName + " to " + tbDest.Text + "\r\n"; 
          fi2.CopyTo(tbDest.Text + @"\" + fi2.Name, true); 
          tbOutput.Text += "Copying " + fi.FullName + " to " + tbDest.Text + "\r\n"; 
          fi.CopyTo(tbDest.Text + @"\" + fi.Name, true); 

          ImageCounter++; 
         } 
         catch (Exception ex) 
         { 
          MessageBox.Show(ex.Message); 
         } 
        } 
       } 

       if (ImageCounter >= MaxImages) 
        break; 

      } 

     } 

什麼情況是,過程中運行正常,但我想更新當文件被複制時,表單上的文本框帶有進度。基本上,表單在運行時會變空,輸出完成後,文本框中會顯示輸出。我想實現一個BackgroundWorker讓它在運行時更新UI。

我已經看過這些例子,但並沒有真正關注它們。我沒有完整的百分比值,我只想更新.Text更改每次迭代並顯示它。我甚至不認爲我需要將實際的複製操作放在不同的線程中,這聽起來像需要與主​​UI線程分開運行。也許我已經完成了這個複雜的工作......有人能把我推向正確的方向嗎?謝謝!

回答

7

如果您使用後臺工作器,則可以使用ReportProgress方法返回任何整數,例如處理的記錄數。它不一定是百分比。然後,在ProgressChanged處理程序中,您可以更新您的文本框。例如。

int count = e.ProgressPercentage; 
textBox1.Text = string.Format("{0} images processed.", count); 

如果你不想使用後臺工作者,你可以在你的循環中調用Application.DoEvents()。這將使UI有機會刷新自身並響應用戶操作。但要小心 - 它會讓你的程序減慢很多,所以你可能只需要每100次迭代就調用一次。

2

UI不更新,因爲您不允許在長時間運行的文件處理循環中處理任何窗口消息。重繪WinForms應用程序以響應在主線程的消息隊列中處理的WM_PAINT消息。

最簡單的解決方案是強制UI更新:嘗試在修改循環內的文本框後調用窗體上的Update()。

您的應用程序仍然會被凍結(無法響應鼠標點擊等),但至少應該在屏幕上顯示進度消息。如果更新顯示器是您真正需要的,那麼請停在這裏。

下一級解決方案將允許您的應用程序處理文件處理循環中待處理的窗口消息。在您的循環中調用Application.DoEvents()(而不是form.Update)。這將允許窗體重新繪製自己的文本輸出更新,並消除您的UI凍結 - 應用程序可以響應鼠標和鍵盤的活動。

儘管如此,用戶可以在當前活動正在進行時單擊啓動當前活動的按鈕 - 重新進入。您至少應該禁用啓動長時間運行的文件處理的菜單或按鈕,以防止重新進入。

第三級解決方案是使用後臺線程進行文件處理。這引入了一系列需要注意的新問題,並且在很多情況下,線程是過度殺傷的。如果您不打算允許用戶在文件處理過程中對您的應用程序進行任何其他操作,那麼將文件處理關閉到後臺線程中沒有太多意義。

+0

的OP已經在正確的軌道,這是使用BackgroundWorker的,它們都創建後臺線程上(當然,它使用了池線程),並處理您在最後一段中提到的一些問題。 – 2010-07-21 20:01:25

+0

當文件處理正在進行時,OP沒有指出用戶在UI中還有其他事情要做,因此後臺線程可能會導致IMO過度使用。 – dthorpe 2010-07-21 20:48:21

13

您與背景工作者在正確的軌道上。下面是我彙集的一個示例,向您展示如何執行此操作。用Form1創建一個新的Windows應用程序。添加4個控件:label1,backgroundWorker1,button1和button2。然後使用這個代碼隱藏。然後,您可以使用ReportProgress userState將您想要的任何內容報告給主線程。在這個例子中,我傳遞一個字符串。 ProgressChanged事件處理程序然後在UI線程上,並更新文本框。

public partial class Form1 : Form 
{ 
    int backgroundInt; 
    public Form1() 
    { 
     InitializeComponent(); 
     backgroundWorker1.WorkerReportsProgress = true; 
    } 

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     label1.Text = e.UserState as string; 
    } 

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     backgroundInt = 1; 
     while (backgroundWorker1.CancellationPending == false) 
     { 
      System.Threading.Thread.Sleep(500); 
      backgroundWorker1.ReportProgress(0, 
       String.Format("I found file # {0}!", backgroundInt)); 
      backgroundInt++; 
     } 
    } 


    private void button1_Click(object sender, EventArgs e) 
    { 
     backgroundWorker1.RunWorkerAsync(); 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     backgroundWorker1.CancelAsync(); 
    } 
} 
+4

關於OP的原始「我沒有百分比」投訴的重要意義在於ReportProgress不僅可以採用百分比,還可以採用通用對象 - 可以(如本例中)一個字符串。 – 2010-07-21 20:03:48

+0

將e.UserState +1作爲字符串。對我來說這遠非明顯。 – Jahmic 2012-12-20 11:55:31

1

只需創建一個(1)的委託來包裝你ProcessImages方法調用,(2)關火呼叫使用委託,和(3)當你想從你的ProcessImages方法更新文本框,檢查跨THEAD操作,並確保你在主線程做了更新:​​

delegate void ProcessImagesDelegate(string did, string sourceFolder, string destFolder, string strNumImages); 

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages) 
{ 
    // do work 

    // update textbox in form 
    if (this.textBox1.InvokeRequired) 
    { 
     this.textBox1.Invoke(new MethodInvoker(delegate() { this.textBox1.Text = "delegate update"; })); 
    } 
    else 
    { 
     this.textBox1.Text = "regular update"; 
    } 

    // do some more work 
} 

public void MyMethod() 
{ 
    new ProcessImagesDelegate(ProcessImages).BeginInvoke(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString(), null, null); 
} 
+0

爲什麼這比使用BackgroundWorker更好? – 2010-07-21 20:02:10

+0

由於他們都使用線程池,沒有一個能夠提供比其他更好的性能,所以我傾向於更喜歡委託人,因爲他們通常會給我更簡潔/更簡單的代碼(imho),而且這只是另一種方式來完成他希望 – Jason 2010-07-21 20:33:39

相關問題