C # Winform ProgressBar и BackgroundWorker

У меня такая проблема:

У меня есть форма с именем MainForm. Мне нужно провести долгую операцию с этой формой.

Пока продолжается эта долгая операция, мне нужно показать другую из ProgressForm поверх MainForm.

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, потому что я не знаю, что произойдет и сколько времени потребуется для завершения операции на этом уровне моего приложения.

Может кто-нибудь помочь мне?


person user366312    schedule 24.09.2009    source источник


Ответы (4)


Одна проблема в том, что вы спите 30 секунд. Обычно вы вызываете ReportProgress на различных этапах вашей длительной задачи. Итак, чтобы продемонстрировать это, вы можете захотеть перевести свой код в режим сна на 1 секунду, но 30 раз, вызывая ReportProgress каждый раз, когда он завершает сон.

Другая проблема заключается в том, что вы показываете свой ProgressForm из фонового потока. Вы должны запустить его в потоке UI, но привязать к нему событие ProgressChanged фонового рабочего. Затем, когда фоновый исполнитель сообщит о ходе выполнения, форма выполнения будет обновлена.

person Jon Skeet    schedule 24.09.2009
comment
Я не должен вносить никаких изменений в MyClass. Coz, я не знаю, что произойдет на этом уровне моего приложения. - person user366312; 24.09.2009
comment
Тогда вы не сможете точно сообщить о прогрессе. Я имею в виду, что вы можете добавить таймер в ProgressForm, чтобы просто увеличивать индикатор выполнения каждую секунду, но это будет только иллюзией. Если у вас нет возможности узнать фактический прогресс, тогда насколько полезен индикатор выполнения? - person Jon Skeet; 24.09.2009

ReportProgress - это метод, который вам нужно будет вызвать в своем «рабочем» методе. Этот метод вызовет событие ProgressChanged.

В своей форме вы можете прикрепить обработчик событий к событию ProgressChanged. Внутри этого обработчика событий вы можете изменить положение индикатора выполнения. Вы также можете присоединить обработчик событий к событию RunWorkerCompleted, и внутри этого обработчика событий вы можете закрыть форму, содержащую индикатор выполнения.

person Frederik Gheysels    schedule 24.09.2009
comment
Думаю, это общее решение. Пожалуйста, прочтите проблему еще раз. - person user366312; 24.09.2009

Следует отметить, что вам необходимо установить

backgroundWorker1.WorkerReportsProgress = true;

если вы хотите, чтобы фоновый рабочий запускал событие ProgressChanged и

backgroundWorker1.WorkerSupportsCancellation = true;

если вы хотите иметь возможность отменить рабочий поток.

Как говорили другие, запустите f.ShowDialog () в потоке пользовательского интерфейса и используйте ProgressChanged для обновления ProgressWindow.

Чтобы заставить ProgressForm самозакрываться, в backgroundWorker1_RunWorkerCompleted вызовите f.Close (); Это означает, что долгая операция завершена, и мы можем закрыть окно.

person Alastair Pitts    schedule 24.09.2009
comment
В чем преимущество вызова ProgressWindow из потока пользовательского интерфейса? - person user366312; 24.09.2009

Здесь необходимо передать классу не всю основную форму, а просто экземпляр вашего 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), чтобы обновить индикатор выполнения или еще что-то в вашей основной форме пользовательского интерфейса!

Это должно работать отлично!

Ознакомьтесь с этой статьей MSDN для получения дополнительной информации, см. Их пример Фибоначчи, это то, что они делают, поскольку CalculateFibonacci - это настраиваемый класс, который отправляет обновления в пользовательский интерфейс основной формы.

Дополнительные сведения см. В классе MSDN BackgroundWorker.

person linnx88    schedule 22.01.2011