C # Winforms Как обновить toolStrip в функции - PullRequest
1 голос
/ 02 апреля 2009

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

private void convertButton_Click(object sender, EventArgs e)
{
    toolStripProgressBar.Visible = true;

    ...

    toolStripProgressBar.Visible = false;
}

Я столкнулся с подобной проблемой с tkinter в Python, и мне пришлось вызвать функцию для обновления простаивающих задач. Есть ли способ сделать это с помощью форм Windows без использования потоков?

Редактировать: На заметке сторон это индикатор выполнения в панели инструментов, который также содержит метку, которая обновляется с текстом строки состояния. Есть ли способ получить метку с левой стороны и индикатор выполнения с другой, а не рядом друг с другом слева?

Ответы [ 7 ]

4 голосов
/ 02 апреля 2009

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

Используйте взамен BackgroundWorker - это легко, и в значительной степени предназначено для индикаторов прогресса. Это избавляет от необходимости использовать отдельный поток и сообщать о ходе выполнения обратно в поток пользовательского интерфейса. Нет необходимости в Control.Invoke и т. Д. - он позаботится об этом за вас.

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

2 голосов
/ 02 апреля 2009

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

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

1 голос
/ 03 апреля 2009

Здесь идут две ссылки, пытающиеся объяснить вам, как все работает: (1) (2)

Теперь я постараюсь объяснить это как можно скорее. Большая часть того, что происходит внутри приложения Windows Form, происходит в одном потоке, обычно в том же потоке, в котором выполняется Main (). Если вы откроете Program.cs, вы увидите, что Main () имеет строку, которая выглядит следующим образом:

Application.Run(new Form1());

Если вы в любой момент отладите приложение и изучите стек вызовов, вы увидите, что оно будет возвращено к этому методу Run. Это означает, что приложение Windows Forms фактически представляет собой непрерывный запуск метода Run. Итак, что делает Run? Run - это очередь сообщений, через которую Windows отправляет ему сообщения. Run затем отправляет эти сообщения в правильные элементы управления, которые сами делают такие вещи, как добавление текста, который соответствует нажатой клавише, перерисовывание себя и т. Д. Обратите внимание, что все это происходит во время бесконечного цикла, выполняющегося вместе с одним потоком, поэтому вы набираете или просто перемещая окно, множество этих сообщений передается в приложение, которое, в свою очередь, обрабатывает их и реагирует соответствующим образом, все в одном потоке. Элементы управления также могут отправлять сообщения себе через очередь, и даже вы можете помещать сообщения в насос через Control.BeginInvoke. Одна из вещей, которую делают эти элементы управления, состоит в том, чтобы вызывать события в соответствии с тем, что происходит. Поэтому, если вы нажмете кнопку, код, который вы написали для обработки этого щелчка, будет в конечном счете и косвенно выполняться методом Application.Run.

Теперь, что происходит с вашим кодом, так это то, что даже если вы изменяете видимое состояние вашего индикатора выполнения на видимое, а затем обновляете его значение, вы затем изменяете его видимость на false, и все это одним и тем же способом. Это означает, что только после выхода из метода Application.Run () сможет продолжить итерацию и использование очереди сообщений, эффективно запрашивая индикатор выполнения для обновления его отображения. Когда это происходит, вы уже оставили видимость индикатора выполнения равным false, последнее, что вы сделали перед выходом из метода. DoEvents () - это быстрый и грязный способ решения вашей проблемы, поскольку он читает сообщения в очереди и обрабатывает их. Мне не очень удобно пользоваться им, так как это может привести к проблемам с повторным входом.

Использование потоков - хорошее решение, но я бы рекомендовал использовать поток ThreadPool вместо пользовательского потока в такой ситуации, так как я склонен использовать пользовательские потоки только в тех случаях, когда у меня ограниченное число долгоживущих потоков и Мне нужно контролировать свои жизненные циклы. Самый простой и практичный способ использования потоков - это использование компонента BackgroundWorker, хотя я бы порекомендовал пройти через понимание того, как выполнять многопоточность Windows Forms с делегатами, если вы действительно хотите понять, что происходит.

1 голос
/ 02 апреля 2009

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

1 голос
/ 02 апреля 2009

Я предполагаю, что проблема в том, что "..." в вашем коде - это длительный процесс. Обновления пользовательского интерфейса не являются мгновенными, но должны проходить через очередь сообщений в окнах и затем отображаться на экране. Очередь прокачивается, и рисование происходит в том же потоке, что и ваши события.

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

Один из способов сделать это - элемент управления BackgroundWorker.

1 голос
/ 02 апреля 2009

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

0 голосов
/ 12 февраля 2017

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

toolStripStatusBar1.PerformStep();
statusStrip1.Refresh();

Это для .NET 4.0. Несмотря на то, что этот вопрос старый, это был первый случай, когда я нашел этот вопрос.

...