我有一个c#windows窗体应用程序,我把它们放在一起。这很简单:\
输入:
应用搜索源文件夹中的文本文件以查找输入的文本字符串;如果找到该字符串,则将该文件和具有相同名称的图像文件复制到目标文件夹。它会根据整数输入多次执行此操作。
所以我有一个按钮,在按钮点击事件中我打电话
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线程分开运行。也许我完全把这复杂化......有人能把我推向正确的方向吗?谢谢!
答案 0 :(得分:14)
你与背景工作者在正确的轨道上。这是我放在一起向您展示如何执行此操作的示例。使用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();
}
}
答案 1 :(得分:7)
如果使用后台工作程序,则可以使用ReportProgress方法返回任何整数,例如处理的记录数。它不一定是百分比。然后,在ProgressChanged处理程序中,您可以更新文本框。 E.g。
int count = e.ProgressPercentage;
textBox1.Text = string.Format("{0} images processed.", count);
如果您不想使用后台工作程序,可以在循环中调用Application.DoEvents()。这将为UI提供刷新自身和响应用户操作的机会。但要注意 - 它会使你的程序变慢,所以你可能只想在每100次迭代时调用它。
答案 2 :(得分:2)
UI不会更新,因为您不允许在长时间运行的文件处理循环中处理任何窗口消息。 WinForms应用程序重绘以响应在主线程的消息队列中处理的WM_PAINT消息。
最简单的解决方案是强制进行UI更新:在修改循环内的文本框后,尝试在表单上调用Update()。
您的应用仍会被UI冻结(无法响应鼠标点击等),但这至少应该会在屏幕上显示进度消息。如果您真正需要更新显示器,请停在此处。
下一级解决方案是允许您的应用程序在文件处理循环中处理待处理的窗口消息。在循环中调用Application.DoEvents()(而不是form.Update)。这将允许表单重新绘制文本输出更新,并将消除您的UI冻结 - 应用程序可以响应鼠标和键盘活动。
但请注意 - 用户可以在当前活动正在进行时单击启动当前活动的按钮 - 重入。您应该至少禁用启动长时间运行文件处理的菜单或按钮,以防止重入。
第三级解决方案是使用后台线程进行文件处理。这引入了一系列您需要注意的新问题,并且在许多情况下线程都是过度的。如果您在文件处理过程中不允许用户对您的应用程序执行任何其他操作,那么将文件处理推送到后台线程没有多大意义。
答案 3 :(得分:1)
只需创建(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);
}
答案 4 :(得分:0)
我不必创建后台工作人员。我所做的只是调用 .Update()
后跟 System.Threading.Thread.Sleep(100)
,然后它开始工作。