2009-09-24 3 views
15

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

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

ответ

12

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

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

+0

Я не должен вносить изменения в MyClass. Coz, я не знаю, что произойдет в этом слое моего приложения. – anonymous

+6

Тогда вы не можете точно сообщить о прогрессе. Я имею в виду, что вы можете добавить таймер в 'ProgressForm', чтобы просто увеличивать индикатор выполнения каждую секунду, но это будет только иллюзией. Если у вас нет никакого способа узнать * фактический * прогресс, то насколько полезен индикатор выполнения? –

2

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

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

+0

Это общее решение, я думаю. Plz снова прочитал проблему. – anonymous

2

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

backgroundWorker1.WorkerReportsProgress = true; 

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

backgroundWorker1.WorkerSupportsCancellation = true; 

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

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

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

+0

В чем преимущество вызова ProgressWindow из потока пользовательского интерфейса? – anonymous

0

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

Для получения более подробной информации см. MSDN BackgroundWorker Class.

Смежные вопросы