1

我正在使用 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)它显示“文件夹已成功隐藏.".During ProgressChanged 事件标签也应该显示百分比,但它没有。请指出或纠正我出错的地方。任何帮助将不胜感激。

4

1 回答 1

2

首先,您BackgroundWorker正在尝试从其后台线程更新 UI,这在多线程 UI 应用程序中是禁止的。

要更新 UI,您需要切换到 UI 线程来进行更新。这是通过首先检查label3.InvokeRequired是否为真(表示您不能从当前线程更新 UI),然后将委托传递给label3.Invoke ( not Delegate.Invoke())

这是您需要非常熟悉的 WinForms 开发模式。Control.Invoke MSDN 页面包含一个利用此模式的示例。

其次,您需要BackgroundWorker.ReportProgress()定期调用,这会触发ProgressChanged具有百分比完成值的事件。


这是一个示例应用程序。它假设您有一个带有按钮、标签和带有WorkgerReportsProgress = true(和WorkerSupportsCancellation = true,用于奖励积分)的 BackgroundWorker 的表单。

当您单击该按钮时,它会启动一个 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();
    }
  }
}
于 2013-08-28T21:19:49.823 回答