【问题标题】:Two issues with backgroundworker with progress bar WPF带有进度条 WPF 的 backgroundworker 的两个问题
【发布时间】:2016-06-01 11:21:37
【问题描述】:

我正在使用 WPF,并且我的主线程是 GUI(向导)。

当用户在向导上单击完成时,它会打开第二个线程,显示后台工作人员中使用的用户进度条。

在我做的主线程中:

MessageWithProgressBar progress = new MessageWithProgressBar();
progress.Show();
createFilesInA();
createFilesInB();
createFilesInC();
createFilesInD();
createFilesInE();
createFilesInF();
createFilesInG();
createFilesInH();
createFilesInI();
createFilesInJ();
createFilesInK();

在每个 createFiles 方法中,我将名为 currentStep 的静态变量递增 1,我在后台工作程序中使用它,如下所述。

我在后台工作:

public partial class MessageWithProgressBar : Window
{
    private BackgroundWorker backgroundWorker = new BackgroundWorker();
    public MessageWithProgressBar()
    {
        InitializeComponent();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.ProgressChanged += ProgressChanged;
        backgroundWorker.DoWork += DoWork;
        backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    }

    private void DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(100);
        int i = GeneralProperties.General.currentStep;
        if (i > GeneralProperties.General.thresholdStep)
        {
            progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = 100;
                                            title.Content = progress.Value.ToString();
                                            return null;
                                        }), null);
            return;
        }
        else
        {
            progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = (int)Math.Floor((decimal)(8 * i));
                                            progressLabel.Text = progress.Value.ToString();
                                            return null;
                                        }), null);
        }
    }

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = e.ProgressPercentage;
                                            return null;
                                        }), null);
    }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = 100;
                                            title.Content = progress.Value.ToString();
                                            return null;
                                        }), null);
        WindowMsgGenDB msg = new WindowMsgGenDB();
        msg.Show();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        if (backgroundWorker.IsBusy == false)
        {
            backgroundWorker.RunWorkerAsync();
        }
    }
}

主线程更新了名为 currentStep 的变量,第二个线程使用它来报告主线程的进度。

主线程的操作需要几秒钟(不超过15秒)

我有两个问题:

  1. 我只在currentStep=2(那么进度为16)然后进度为100时才在进度条上看到,而且我看不到每一步

  2. 一开始,进度条卡住了,好像卡住了。

(也许它从主线程连接到调用progress.Show()?)

谢谢!

【问题讨论】:

  • 您是否通过绑定设置进度条的值?还是直接?
  • 直接@Nudity。见上面的代码。
  • 是的 ^^ - 当前步长每次迭代都增加 1?
  • @Nudity 是的,currentStep 从 1 变为 thresholdStep = 12。progressBar.Value 从 8 变为 96,然后由等式 8*i 变为 100,而 i 由 currentStep 改变。
  • 这里没有增加 i 并且从不使用 ProgressChanged,因为你从不调用 ReportProgress。所有这些代码工作者要做的就是将进度条设置为 100 或 x 8,然后完成。如果这不是您所看到的,那么您需要提供minimal reproducible example

标签: c# wpf multithreading backgroundworker


【解决方案1】:

据我了解您的代码,您的后台工作人员实际上并没有做任何事情。它会更新进度一次,仅此而已。

另外:使用全局静态变量在表单和后台工作人员之间进行通信 - 哎哟...

另外,我认为你用错了。工作 (CreateFilesInA ... CreateFilesInK) 应该由后台工作人员完成 - 这就是它的用途。由于主线程将按照您实现它的方式被阻塞,否则您将看不到任何更新。

实现类似这样的通常方法是:

  1. 创建进度窗口并禁用 UI
  2. 启动在DoWork 中执行操作的后台工作程序。在DoWork 中,每次调用CreateFilesInXYZ 方法后,调用ReportProgress 以更新UI。
  3. 每当ProgressChanged 事件被触发时更新正在进行的窗口
  4. 后台工作完成后隐藏进度窗口并启用应用程序的 UI

您的操作方式绝不是异步的。所以,实际上,你的代码应该是这样的:

public partial class MainWindow : Window
{
    private BackgroundWorker backgroundWorker = new BackgroundWorker();
    private MessageWithProgressBar progressWindow;

    public MainWindow()
    {
        InitializeComponent();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.ProgressChanged += ProgressChanged;
        backgroundWorker.DoWork += DoWork;
        backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        progressWindow = new MessageWithProgressBar();
        progressWindow.Owner = this;
        progressWindow.Show();

        backgroundWorker.RunWorkerAsync();
    }


    private void DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
        int numSteps = 11;
        int currentStep = 0;
        int progress = 0;

        CreateFilesInA();
        currentStep += 1;

        progress = (int)((float)currentStep / (float)numSteps * 100.0);
        worker.ReportProgress(progress);

        CreateFilesInB();
        currentStep += 1;

        progress = (int)((float)currentStep / (float)numSteps * 100.0);
        worker.ReportProgress(progress);

        // All other steps here
        ...
    }

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressWindow.progress.Value = e.ProgressPercentage;
    }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progressWindow.Close();

        WindowMsgGenDB msg = new WindowMsgGenDB();
        msg.Show();
    }
}

请注意,上面的代码会进入您的主窗口! MessageWithProgressWindow 不包含任何代码。也许Window_Loaded 事件处理程序不是启动后台工作程序的正确位置,但您明白了。

【讨论】:

  • 如何在主窗口中为 MessageWithProgressBar 定义一个 ctor?我得到一个方法必须返回类型的错误
  • 我真的不明白这个问题。 MessageWithProgressBar 类默认有一个无参数的构造函数。您已经在上面的代码中调用了它。请使用相关代码更新您的问题。
  • @ThorstenDittmar 我怀疑这是一个简单的错字。您的 MainWindow 有一个名称错误的构造函数。
  • 查看固定代码。上面的代码不会影响进度窗口。进度窗口只是一个没有实际代码的窗口。公开进度条或创建返回进度条的属性。不再。上面的代码进入你的主窗口。
  • “不起作用”不是有效的问题描述。如果您提供有关问题所在的详细信息,我会很乐意修改答案。
猜你喜欢
  • 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
相关资源
最近更新 更多