Индикатор выполнения отображается только после завершения работы? - PullRequest
0 голосов
/ 27 июля 2010

Я пытаюсь, чтобы индикатор выполнения отображался в форме, но по какой-то причине форма фактически не видна, пока процесс не завершен, и закрывается, когда процесс завершен (или, другими словами, форма открыта только на мгновение). Как сделать так, чтобы форма отображалась в начале процесса?

Примечание: мой код может быть не на 100% правильным, я просто пытаюсь сделать его отличным от моего по соображениям конфиденциальности.

public void SpawnPizzaProgressBarForm(object sender, EventArgs e)
{
    FormPizzaProgressBar Form = new FormPizzaProgressBar();
    Form.ShowDialog();
}
...
public void ProgressBarForm_Load(object sender, EventArgs e)
{
    Pizza = new Pizza();
    Pizza.Eat(PizzaEatingProgressBar);
    this.Close();
}
...
public void Eat(ProgressBar PizzaEatingProgressBar)
{
    foreach(var Slice in Pizza)
    {
        Slice.Clear(); //
        PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100
    }
}

Ответы [ 5 ]

6 голосов
/ 27 июля 2010

Это происходит потому, что вы выполняете всю обработку в событии загрузки для формы .Это называется до , когда форма показывается впервые.

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

То, что вы хотите сделать, это использовать экземпляр BackgroundWorker для выполнения вашей работы.Для этого необходимо сделать следующее:

  • Создать экземпляр BackgroundWorker
  • Установить для свойства WorkerSupportsProgress значение true (по умолчаниюfalse)
  • Подписаться на событие DoWork
  • В обработчике событий DoWork вызовите метод ReportProgress , чтобы сообщить о своем прогрессе
  • Подпишитесь на событие ProgressChanged , здесь вы будете обновлять свой индикатор выполнения.Это также событие htat, которое вызывается при вызове метода ReportProgrss, упомянутого ранее
  • Подписаться на событие RunWorkerCompleted , здесь вы закроете форму, когда закончите
  • Вызовите метод RunWorkerAsync , чтобы запустить асинхронную операцию

У вас есть некоторые проблемы, связанные с тем, что класс Pizza тесно связан с индикатором выполнения.Это не очень хорошая идея.Скорее, у вас должно быть событие, которое вызывается, чтобы показать, что прогресс изменился, а затем вызвать событие ProgressChanged из обработчика событий для вашего экземпляра Pizza.

Я удалил код для вашего метода Eatи заключил его в форму, чтобы показать вам пример использования класса BackgroundWorker, но идеальным решением было бы выставить событие, чтобы указать, когда количество потребляемой пиццы изменяется.

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

Вот как выглядит пример:

public void SpawnPizzaProgressBarForm(object sender, EventArgs e)
{
    FormPizzaProgressBar Form = new FormPizzaProgressBar();
    Form.ShowDialog();
}

...

BackgroundWorker worker = new BackgroundWorker();

public void ProgressBarForm_Load(object sender, EventArgs e)
{
    // Initialize the background worker.
    worker = new BackgroundWorker();

    // Indicate that the worker supports progress.
    worker.WorkerSupportsProgress = true;

    // Subscribe to the DoWork event.
    worker.DoWork += (s, e) => {
        // Create the pizza instance.
        Pizza = new Pizza();

        // Process the slices.
        foreach (var Slice in Pizza)
        {
            // Clear the slice.
            Slice.Clear();

            // Report the progress.
            worker.ReportProgress(Slice.Index / Pizza.Count() * 100);
        }
    };

    // Subscribe to the ProgressChanged event.
    worker.ProgressChanged = (s, e) => {
        // Update the progress bar.
        PizzaEatingProgressBar.Value = e.ProgressPercentage;
    };

    // Subscribe to the RunWorkerCompleted event.
    worker.RunWorkerCompleted = (s, e) => {
        // Close the dislog.
        this.Close();
    };
}

// Must override to properly dispose of the background worker.
protected override void Dispose(bool disposing)
{
    // Call the base.
    base.Disposing(disposing);

    // Dispose of the background worker if disposing is true.
    if (disposing) worker.Dispose();
}
3 голосов
/ 27 июля 2010

Winforms основан на Windows API, который не обновляет элементы графического интерфейса, если поток, в котором были созданы элементы графического интерфейса, не «прокачан». WinForms вызвал ваш метод ProgressForm_Load в потоке, в котором он должен «качать» сообщения для обновления элементов графического интерфейса. Вы не качаете очередь сообщений во время своей операции в Eat. Поэтому, если элементы графического интерфейса не обновляются, вы не увидите изменения индикатора выполнения.

самое простое решение - периодически вызывать Application.DoEvents в вашем методе Eat. Лучший ответ - выполнить длинную операцию в другом потоке, отобразить песочные часы во время этой операции и периодически информировать индикатор выполнения для обновления, используя стиль обновления графического интерфейса Invoke (вы не можете вызывать методы большинства элементов графического интерфейса). непосредственно из потока, отличного от потока, в котором они были созданы).

2 голосов
/ 27 июля 2010
 PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100

Это не работает, если Slice.Index как целое число. Вам нужно разыграть удвоение, чтобы получить деление с плавающей запятой. Например:

 PizzaEatingProgressBar.Value = (int)((double)Slice.Index / Pizza.Count) * 100);

или сделайте так:

 PizzaEatingProgressBar.Value = (Slice.Index * 100) / Pizza.Count;

Что гарантирует, что деление не может обрезаться до 0, как это делало ваше оригинальное выражение. Вопреки всем советам, ProgressBar делает сам рисовать, когда вы изменяете свойство Value. Хотя ваше окно все еще кататонично.

1 голос
/ 27 июля 2010

Это похоже на замкнутый цикл - вам нужно либо поместить работу в фоновый поток, используя BackgroundWorker, либо использовать чит и дать UI время для обновления, вызвав Application.DoEvents() (обрабатывает очередь сообщений, которая включает в себя рисованиекоманды).

Если цикл слишком узкий или работа слишком тяжелая, пользовательский интерфейс будет в основном блокироваться до его завершения, даже если изменения пользовательского интерфейса перемешаны.

0 голосов
/ 27 июля 2010

Вам нужно использовать многопоточность, чтобы пользовательский интерфейс обновлялся, пока BackgroundWorker обрабатывает ваш фактический процесс.Прочтите эту статью (от уважаемого Джона Скита), прежде чем начать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...