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

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

Ответы

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

backgroundWorker1.WorkerReportsProgress = true;

если вы хотите, чтобы работник фона поднимал событие ProgressChanged и

backgroundWorker1.WorkerSupportsCancellation = true;

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

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

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

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

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

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

Подробнее см. MSW BackgroundWorker Class.