我正在使用C#和Winforms进行存档并将文件夹保存到指定位置,对于存档文件夹,我有一个 BackgroundWorker ,它将文件夹路径作为输入并生成一个zip存档。现在下一步该文件需要在指定的位置移动,再次因为文件足够大并且可以挂起UI线程我将代码移动到另一个名为 FileMove 的 BackgroundWorker ,一切顺利,但 FileMove 没有报告任何进展,这是我在归档结束后立即调用的函数;
private void FileMove_DoWork(object sender, DoWorkEventArgs e)
{
label3.Text = "Saving file,please wait...";
File.Move(temppath + @"\Output.jpg", savefilename);
}
private void FileMove_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label3.Text = "Saving file,please wait... " + e.ProgressPercentage.ToString(); //This should show Progress Percentage but it doesn't.
}
private void FileMove_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label3.Text = ("The folder has been successfully hidden.");
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
this.ControlBox = true;
}
我面临的问题是,一旦文件移动开始 label3 显示“保存文件,请稍候......”并且经过很长时间(因为我压缩900-1000 MB) )它显示“文件夹已被成功隐藏。”。在ProgressChanged事件标签期间,标签也应显示百分比,但它不显示。请指出或纠正我出错的地方。任何帮助将不胜感激。
答案 0 :(得分:2)
首先,您的BackgroundWorker
正在尝试从其后台线程更新UI,这在多线程UI应用程序中是禁止的。
要更新UI,您需要将其切换到UI线程以进行更新。首先检查label3.InvokeRequired是否为真(表示您无法从当前线程更新UI),然后将委托传递给label3.Invoke(not Delegate.Invoke()
)
这是您需要非常熟悉WinForms开发的模式。 Control.Invoke MSDN页面包含了利用此模式的示例。
其次,您需要定期致电BackgroundWorker.ReportProgress()
,这会以完整百分比值触发ProgressChanged
事件。
这是一个示例应用。它假定您有一个带有Button,Label和BackgroundWorker的表单WorkgerReportsProgress = true
(和WorkerSupportsCancellation = true
,奖励积分)。
当您单击按钮时,它会启动一个5秒的阻止任务,然后切换到10个1秒的阻止任务(沿途报告进度)。
请注意帮助方法InvokeIfRequired()
,确保从正确的线程更新UI。过度使用这一点没有任何害处。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
label1.Text = "Reset!";
backgroundWorker1.CancelAsync();
return;
}
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
label1.InvokeIfRequired(() => label1.Text = "Gettin busy");
System.Threading.Thread.Sleep(5000);
for (int i = 0; i < 10; i++)
{
backgroundWorker1.ReportProgress(i*10);
System.Threading.Thread.Sleep(1000);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.InvokeIfRequired(() => label1.Text = "Done");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.InvokeIfRequired(() => label1.Text = string.Format("{0}% done", e.ProgressPercentage));
}
}
public static class InvokeExtensions
{
public static void InvokeIfRequired(this ISynchronizeInvoke control, MethodInvoker action)
{
if (control.InvokeRequired)
{
control.Invoke(action, null);
}
else
{
action();
}
}
}