【问题标题】:BackgroundWorker doesn't report filecopy progress?BackgroundWorker 不报告文件复制进度?
【发布时间】:2013-08-29 05:55:43
【问题描述】:

我正在使用 C# 和 Winforms 制作和应用程序,将文件夹存档并保存到指定位置,对于存档文件夹,我有一个 BackgroundWorker 将文件夹路径作为输入并生成一个 zip 存档。现在在下一步需要将文件移动到指定位置,因为文件足够大并且可能会挂起 UI 线程,所以我将代码移动到另一个名为 FileMoveBackgroundWorker,一切正常,除了 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 期间事件标签也应该显示百分比,但它没有。请指出或纠正我出错的地方。任何帮助将不胜感激。

【问题讨论】:

  • 您是否已启用 bw 工作人员报告进度? - this
  • 是的,BackgroundWorker 已启用报告进度。
  • 那可能是因为你的标签在ui线程上,你需要invoke it
  • 我认为您无法像使用File.Move() 所想的那样报告进度。无论您的BackgroundWorker() 做什么,它都需要能够定期调用ReportProgress() msdn.microsoft.com/en-us/library/ka89zff4.aspx
  • 这篇文章可能会有所帮助:listener.com.ba/thepage/2011/01/22/…。作者展示了将文件移动(或在他的情况下是复制)分解成更小的块的代码,使他能够控制并在每个块之后调用ReportProgress()。希望对您有所帮助。

标签: c# multithreading winforms file


【解决方案1】:

首先,您的 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();
    }
  }
}

【讨论】:

  • -1 您的 BackgroundWorker 正在尝试从其后台线程更新 UIProgressChanged 中更改 UI 事件是 backgroundworker 的方式应该工作。正如@David Dansey 注意到的那样,由于 File.move() 方法不报告进度(它不会调用 reportProgress 方法本身!)所以没有进展......
  • 已更新以解决这两个问题。该示例始终保持响应并报告进度
  • 感谢您考虑我的评论 :) 仍然有一些事情让我感到不安。当你说 你的 BackgroundWorker 正在尝试从其后台线程更新 UI 时,我同意你的看法,因为这段代码试图在 DoWork 方法中更新 label.text,因此使用 InvokeExtension 可以感觉在这种情况下。但从文档来看,ProgressChangedRunWorkerCompleted 事件旨在与用户界面通信,所以我猜它在 UI 线程中。我从未在我的后台工作人员实施中使用过Invoke
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多