简体   繁体   English

C#Winform ProgressBar和BackgroundWorker

[英]C# Winform ProgressBar and BackgroundWorker

I have the following problem: 我有以下问题:

I have a Form named MainForm. 我有一个名为MainForm的表单。 I have a long operation to be taken place on this form. 我在这张表格上进行了长时间的操作。

While this long operation is going on, I need to show another from named ProgressForm on top of the MainForm. 虽然这个长时间的操作正在进行,但我需要在MainForm之上显示另一个名为ProgressForm的操作。

ProgressForm contains a progress bar which needs to be updated while the long operation is taking place. ProgressForm包含一个进度条,需要在长时间操作时进行更新。

After the long operation is completed, the ProgressForm should be closed automatically. 长操作完成后,应自动关闭ProgressForm。

I have written the following code: 我写了以下代码:

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();
        }
    }
}

I can't find a way to update the progressBar . 我找不到更新progressBar

Where should I place backgroundWorker1.ReportProgress() and how should I call this? 我应该在哪里放置backgroundWorker1.ReportProgress()以及如何调用它?

I must not make any change in MyClass because I don't know what would happen or how long it takes to complete the operation in this layer of my application. 我不能在MyClass做任何更改,因为我不知道在我的应用程序的这一层中完成操作会发生什么或需要多长时间。

Can anyone help me? 谁能帮我?

One problem is that you're sleeping for 30 seconds. 一个问题是你睡了30秒。 Normally you'd call ReportProgress at various points within your long-running task. 通常,您可以在长时间运行的任务中的不同位置调用ReportProgress So to demonstrate this, you might want to change your code to sleep for 1 second, but 30 times - calling ReportProgress each time it finishes a sleep. 因此,为了证明这一点,您可能希望将代码更改为休眠1秒,但需要30次 - 每次完成睡眠时调用ReportProgress

Another problem is that you're showing your ProgressForm from the background thread. 另一个问题是你后台线程中显示你的ProgressForm You should start it in the UI thread, but hook the background worker's ProgressChanged event to it. 您应该在UI线程中启动它,但是将后台worker的ProgressChanged事件挂钩到它。 Then when the background worker reports progress, the progress form will be updated. 然后,当后台工作人员报告进度时,将更新进度表单。

ReportProgress is the method which you'll have to call in your 'working' method. ReportProgress是您必须在“工作”方法中调用的方法。 This method will raise the 'ProgressChanged' event. 此方法将引发“ProgressChanged”事件。

In your form, you can attach an eventhandler to the ProgressChanged event. 在表单中,您可以将事件处理程序附加到ProgressChanged事件。 Inside that eventhandler, you can change the position of the progressbar. 在事件处理程序内部,您可以更改进度条的位置。 You can also attach an eventhandler to the RunWorkerCompleted event, and inside that eventhandler, you can close the form which contains the progressbar. 您还可以将事件处理程序附加到RunWorkerCompleted事件,并在该事件处理程序内部,您可以关闭包含进度条的表单。

It should be noted you need to set 应该注意你需要设置

backgroundWorker1.WorkerReportsProgress = true;

if you want the background worker to raise the ProgressChanged event and 如果你想让后台工作者提升ProgressChanged事件和

backgroundWorker1.WorkerSupportsCancellation = true;

if you wish to be able to cancel the worker thread. 如果您希望能够取消工作线程。

As the others have said, run the f.ShowDialog() in your UI thread and use ProgressChanged to update the ProgressWindow. 正如其他人所说,在UI线程中运行f.ShowDialog()并使用ProgressChanged更新ProgressWindow。

To get the ProgressForm to self close, in backgroundWorker1_RunWorkerCompleted, call f.Close(); 要使ProgressForm自行关闭,请在backgroundWorker1_RunWorkerCompleted中调用f.Close(); This means the long operation has completed and we can close the window. 这意味着长操作已经完成,我们可以关闭窗口。

What is needed here is to not pass the entire main form to the class, but simply the instance of your BackgroundWorker! 这里需要的是不将整个主窗体传递给类,而只是将BackgroundWorker的实例传递给它! You need to cast the sender as Background worker like so: 你需要将发送者强制转换为后台工作者,如下所示:

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.
}

And then in your MyClass.MyMethod() you would do the progress calculations and simply raise the worker.ReportProgress(int percentage) event to update the progress bar or whatnot on your main UI form! 然后在你的MyClass.MyMethod()你将进行进度计算并简单地引发worker.ReportProgress(int percentage)事件来更新主UI表单上的进度条或诸如此类的东西!

This should work perfectly! 这应该完美!

Check out this MSDN article for more details, see their Fibonacci example, it's what they do since CalculateFibonacci is a custom class which sends updates to Main form's UI. 查看这篇MSDN文章了解更多详细信息,请参阅他们的Fibonacci示例,这是他们的工作,因为CalculateFibonacci是一个自定义类,它向主窗体的UI发送更新。

See MSDN BackgroundWorker Class for more details. 有关更多详细信息,请参阅MSDN BackgroundWorker类

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM