Показывать индикатор выполнения при выполнении какой-либо работы в C #? - PullRequest
26 голосов
/ 23 декабря 2009

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

У меня есть WinForm ProgressForm с ProgressBar, который будет продолжаться бесконечно шатер .

using(ProgressForm p = new ProgressForm(this))
{
//Do Some Work
}

Теперь есть много способов решить проблему, например, используя BeginInvoke, дождитесь завершения задачи и наберите EndInvoke. Или используя BackgroundWorker или Threads.

У меня есть некоторые проблемы с EndInvoke, хотя это не вопрос. Вопрос в том, какой самый лучший и самый простой способ, которым вы пользуетесь в таких ситуациях, когда вы должны показать пользователю, что программа работает и не отвечает, и как вы справляетесь с этим с помощью максимально простого кода, который эффективен и выиграл ». t утечка, и может обновить графический интерфейс.

Как и BackgroundWorker, необходимо иметь несколько функций, объявлять переменные-члены и т. Д. Кроме того, необходимо сохранить ссылку на форму ProgressBar и избавиться от нее.

Редактировать : BackgroundWorker не является ответом, поскольку может случиться так, что я не получу уведомление о прогрессе, что означает, что не будет никакого вызова к ProgressChanged, поскольку DoWork является одиночный вызов внешней функции, но мне нужно продолжать вызывать Application.DoEvents();, чтобы индикатор выполнения продолжал вращаться.

Награда за лучшее решение кода для этой проблемы. Мне просто нужно вызвать Application.DoEvents(), чтобы индикатор выполнения Marque работал, а рабочая функция работала в главном потоке и не возвращала уведомление о ходе выполнения. Мне никогда не требовался магический код .NET для автоматического отчета о прогрессе, мне просто нужно лучшее решение, чем:

Action<String, String> exec = DoSomethingLongAndNotReturnAnyNotification;
IAsyncResult result = exec.BeginInvoke(path, parameters, null, null);
while (!result.IsCompleted)
{
  Application.DoEvents();
}
exec.EndInvoke(result);

, который поддерживает индикатор выполнения (означает не замораживание, а обновление маркера)

Ответы [ 13 ]

0 голосов
/ 23 декабря 2009

Прочитать ваши требования самым простым способом было бы отобразить форму без режима и использовать стандартный таймер System.Windows.Forms для обновления хода выполнения в форме без режима. Нет потоков, нет возможных утечек памяти.

Поскольку для этого используется только один поток пользовательского интерфейса, вам также необходимо вызывать Application.DoEvents () в определенные моменты во время основной обработки, чтобы гарантировать визуальное обновление индикатора выполнения.

0 голосов
/ 23 декабря 2009

Мы используем модальную форму с BackgroundWorker для такой вещи.

Вот быстрое решение:

  public class ProgressWorker<TArgument> : BackgroundWorker where TArgument : class 
    {
        public Action<TArgument> Action { get; set; }

        protected override void OnDoWork(DoWorkEventArgs e)
        {
            if (Action!=null)
            {
                Action(e.Argument as TArgument);
            }
        }
    }


public sealed partial class ProgressDlg<TArgument> : Form where TArgument : class
{
    private readonly Action<TArgument> action;

    public Exception Error { get; set; }

    public ProgressDlg(Action<TArgument> action)
    {
        if (action == null) throw new ArgumentNullException("action");
        this.action = action;
        //InitializeComponent();
        //MaximumSize = Size;
        MaximizeBox = false;
        Closing += new System.ComponentModel.CancelEventHandler(ProgressDlg_Closing);
    }
    public string NotificationText
    {
        set
        {
            if (value!=null)
            {
                Invoke(new Action<string>(s => Text = value));  
            }

        }
    }
    void ProgressDlg_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        FormClosingEventArgs args = (FormClosingEventArgs)e;
        if (args.CloseReason == CloseReason.UserClosing)
        {
            e.Cancel = true;
        }
    }



    private void ProgressDlg_Load(object sender, EventArgs e)
    {

    }

    public void RunWorker(TArgument argument)
    {
        System.Windows.Forms.Application.DoEvents();
        using (var worker = new ProgressWorker<TArgument> {Action = action})
        {
            worker.RunWorkerAsync();
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;                
            ShowDialog();
        }
    }

    void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            Error = e.Error;
            DialogResult = DialogResult.Abort;
            return;
        }

        DialogResult = DialogResult.OK;
    }
}

И как мы это используем:

var dlg = new ProgressDlg<string>(obj =>
                                  {
                                     //DoWork()
                                     Thread.Sleep(10000);
                                     MessageBox.Show("Background task completed "obj);
                                   });
dlg.RunWorker("SampleValue");
if (dlg.Error != null)
{
  MessageBox.Show(dlg.Error.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
dlg.Dispose();
0 голосов
/ 23 декабря 2009

Используйте компонент BackgroundWorker, который предназначен именно для этого сценария.

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

...