C#使用backgroundworker进程更新并附加文本框值

我有一个ac #windows form app我一起扔了。 这很简单:

输入:

  • 文字字符串
  • 源文件夹路径
  • 目标文件夹路径
  • 整数计数

该应用程序搜索源文件夹中的文本文件以查找输入的文本字符串; 如果找到该字符串,则将该文件和具有相同名称的图像文件复制到目标文件夹。 它会根据整数输入多次执行此操作。

所以我有一个按钮,在按钮点击事件我打电话

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线程分开运行。 也许我完全把这复杂化了……有人能把我推向正确的方向吗? 谢谢!

如果使用后台工作程序,则可以使用ReportProgress方法返回任何整数,例如处理的记录数。 它不一定是百分比。 然后,在ProgressChanged处理程序中,您可以更新文本框。 例如

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

如果您不想使用后台工作程序,可以在循环中调用Application.DoEvents()。 这将为UI提供刷新自身和响应用户操作的机会。 但要注意 – 它会使你的程序变慢,所以你可能只想在每100次迭代时调用它。

你和背景工作者在正确的轨道上。 这是我放在一起向您展示如何执行此操作的示例。 使用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(); } } 

UI不会更新,因为您不允许在长时间运行的文件处理循环中处理任何窗口消息。 WinForms应用程序重绘以响应在主线程的消息队列中处理的WM_PAINT消息。

最简单的解决方案是强制UI更新:在修改循环内的文本框后,尝试在表单上调用Update()。

您的应用仍然会被UI冻结(对鼠标点击无响应等),但至少应该在屏幕上绘制进度消息。 如果你真正需要更新显示器,那就停在这里。

下一级解决方案是允许您的应用程序在文件处理循环中处理挂起的窗口消息。 在循环中调用Application.DoEvents()(而不是form.Update)。 这将允许表单重新绘制文本输出更新,并将消除您的UI冻结 – 应用程序可以响应鼠标和键盘活动。

但请注意,用户可以在当前活动正在进行时单击启动当前活动的按钮 – 重入。 您应该至少禁用启动长时间运行文件处理的菜单或按钮以防止重入。

第三级解决方案是使用后台线程进行文件处理。 这引入了一系列您需要注意的新问题,并且在许多情况下线程过度。 如果您在文件处理过程中不允许用户对您的应用程序执行任何其他操作,那么将文件处理推送到后台线程没有多大意义。

只需创建一个(1)委托来包装你的ProcessImages方法调用,(2)使用委托来触发调用,以及(3)当你想从你的ProcessImages方法更新文本框时,检查交叉操作并制作确定你从主线程进行更新:

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