【问题标题】:C# Winform ProgressBar and BackgroundWorkerC# Winform ProgressBar 和 BackgroundWorker
【发布时间】:2010-12-01 01:15:36
【问题描述】:

我有以下问题:

我有一个名为 MainForm 的表单。我要在这张表格上进行长时间的手术。

虽然这个漫长的操作正在进行,但我需要在 MainForm 顶部显示另一个名为 ProgressForm 的操作。

ProgressForm 包含一个进度条,在进行长操作时需要更新该进度条。

长操作完成后,ProgressForm应该会自动关闭。

我写了以下代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ClassLibrary
{
    public class MyClass
    {
        public static string LongOperation()
        {
            Thread.Sleep(new TimeSpan(0,0,30));

            return "HelloWorld";
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BackgroungWorker__HelloWorld
{
    public partial class ProgressForm : Form
    {
        public ProgressForm()
        {
            InitializeComponent();
        }

        public ProgressBar ProgressBar
        {
            get { return this.progressBar1; }
            set { this.progressBar1 = value; }
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using ClassLibrary;

namespace BackgroungWorker__HelloWorld
{
    public partial class MainForm : Form
    {
        ProgressForm f = new ProgressForm();

        public MainForm()
        {
            InitializeComponent();  
        }

        int count = 0;
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (f != null)
            {
                f.ProgressBar.Value = e.ProgressPercentage;
            }

            ++count;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled) 
            {  
                MessageBox.Show("The task has been cancelled");  
            }  
            else if (e.Error != null)  
            {                  
                MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());  
            }  
            else 
            {  
                MessageBox.Show("The task has been completed. Results: " + e.Result.ToString());  
            }
        }


        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            if (f == null)
            {
                f = new ProgressForm();
            }

            f.ShowDialog();

            //backgroundWorker1.ReportProgress(100);

            MyClass.LongOperation();

            f.Close();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();

            this.Close();
        }
    }
}

我找不到更新progressBar 的方法。

我应该把backgroundWorker1.ReportProgress()放在哪里,我应该怎么称呼它?

我不能对MyClass 进行任何更改,因为我不知道会发生什么或需要多长时间才能完成我的应用程序这一层的操作。

谁能帮帮我?

【问题讨论】:

    标签: c# winforms progress-bar backgroundworker


    【解决方案1】:

    一个问题是你睡了 30 秒。通常,您会在长期运行的任务中的各个时间点调用ReportProgress。因此,为了演示这一点,您可能希望将代码更改为睡眠 1 秒,但 30 次 - 每次完成睡眠时调用 ReportProgress

    另一个问题是您正在显示您的ProgressForm 来自 后台线程。您应该在 UI 线程中启动它,但将后台工作人员的 ProgressChanged 事件挂钩到它。那么当后台worker汇报进度时,进度表就会更新。

    【讨论】:

    • 我不能对 MyClass 进行任何更改。因为,我不知道我的应用程序的这一层会发生什么。
    • 那你就不能准确的汇报进度了。我的意思是您可以在ProgressForm 中添加一个计时器,以每秒增加进度条,但这只会提供一种错觉。如果您无法找到实际进度,那么进度条有多大用处?
    【解决方案2】:

    ReportProgress 是您必须在“工作”方法中调用的方法。 此方法将引发“ProgressChanged”事件。

    在您的表单中,您可以将事件处理程序附加到 ProgressChanged 事件。在该事件处理程序中,您可以更改进度条的位置。 您还可以将事件处理程序附加到 RunWorkerCompleted 事件,然后在该事件处理程序内,您可以关闭包含进度条的表单。

    【讨论】:

    • 我猜这是一个通用的解决方案。请再次阅读问题。
    【解决方案3】:

    需要注意的是需要设置

    backgroundWorker1.WorkerReportsProgress = true;
    

    如果您希望后台工作人员引发 ProgressChanged 事件并且

    backgroundWorker1.WorkerSupportsCancellation = true;
    

    如果您希望能够取消工作线程。

    正如其他人所说,在您的 UI 线程中运行 f.ShowDialog() 并使用 ProgressChanged 更新 ProgressWindow。

    要让 ProgressForm 自行关闭,请在 backgroundWorker1_RunWorkerCompleted 中调用 f.Close();这意味着长时间的操作已经完成,我们可以关闭窗口了。

    【讨论】:

    • 从 UI 线程调用 ProgressWindow 有什么好处?
    【解决方案4】:

    这里需要的是不要将整个主窗体传递给类,而只是传递 BackgroundWorker 的实例!您需要像这样将发件人转换为后台工作人员:

    private void bgWorker_DoWork(object sender DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event
        BackgroundWorker worker = sender as BackgroundWorker;
    
        // And now you just send worker to whatever class you need like so:
        bgArgs args = e.Argument as bgArgs;
        MyClass objMyClass = new MyClass();
    
        MyClass.MyMethod(strValue, args.Option, worker);
    
        // Do something based on return value.
    }
    

    然后在您的MyClass.MyMethod() 中,您将进行进度计算并简单地引发worker.ReportProgress(int percentage) 事件以更新您的主 UI 表单上的进度条或其他内容!

    这应该可以完美运行!

    查看这篇 MSDN 文章了解更多详细信息,查看他们的斐波那契示例,这是他们所做的,因为 CalculateFibonacci 是一个自定义类,它向主窗体的 UI 发送更新。

    更多详情请见MSDN BackgroundWorker Class

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      相关资源
      最近更新 更多