Здесь идут две ссылки, пытающиеся объяснить вам, как все работает:
(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 с делегатами, если вы действительно хотите понять, что происходит.