Я хотел бы обновить элемент управления ProgressBar в цикле, который выполняет какой-то длительный процесс, например. что-то вроде этого для примера:
for (int p = 0; p < 100; p++)
{
progBar.Value = p;
// do some work here
}
Но если я попробую это внутри метода (например, обработчик нажатия кнопки), индикатор выполнения не будет увеличиваться до тех пор, пока метод не завершится, что противоречит цели включения индикатора выполнения в пользовательский интерфейс.
Есть ли способ заставить индикатор выполнения перерисовывать/перерисовывать внутри цикла, чтобы пользовательский интерфейс немедленно отражал его состояние?
Может быть, это старомодный подход к созданию пользовательского интерфейса, и в наши дни есть способы получше? Все советы оценены.





Существует только один поток пользовательского интерфейса, и его нельзя блокировать.
Таким образом, решение состоит в том, чтобы перенести трудоемкую работу в фоновый поток, например, с помощью Task.Run. Но вы не можете обновить пользовательский интерфейс непосредственно из этого фонового потока.
Чтобы обновить индикатор выполнения, вам нужно какое-то общее поле. Один из способов сделать это — использовать класс Progress, ваша фоновая работа должна вызывать OnReport, и вы должны зарегистрировать обработчик событий для ProgressChanged. Класс гарантирует, что событие возникнет в потоке пользовательского интерфейса. Я предполагаю, что это или что-то очень похожее доступно в WinUI3, но я не очень хорошо знаком с winui.
Другой подход заключается в использовании общего изменчивого поля и использовании таймера (того, который тикает в потоке пользовательского интерфейса) для периодического обновления индикатора выполнения из общего поля.
Ну, вы не можете делать две вещи, например запускать цикл и обновлять ProgressBar, одновременно в одном и том же потоке.
Хитрость заключается в том, чтобы выполнить цикл в фоновом потоке:
DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread();
await Task.Run(() =>
{
for (int p = 0; p < 100; p++)
{
Thread.Sleep(1500); // some long-running work...
dispatcherQueue.TryEnqueue(() => progBar.Value = p);
}
});
Или, по крайней мере, выполните «длительный процесс» в фоновом потоке:
for (int p = 0; p < 100; p++)
{
await Task.Run(() => Thread.Sleep(1500)); // some long-running work...
progBar.Value = p;
}
Привяжите ProgressBar.Value к некоторому свойству класса, который реализует INotifyPropertyChanged (обычно ViewModel привязан к DataContext родительского окна/страницы), затем запустите новую задачу (Task.Run) с вашим циклом и измените связанное свойство...