Большинство вопросов, которые я вижу по этому поводу, задают противоположное — как обновить пользовательский интерфейс из потока, не связанного с пользовательским интерфейсом, или из фонового потока.
В моем случае я хочу наоборот. Есть ли что-нибудь, что я могу вызвать или сделать в команде, работающей в потоке пользовательского интерфейса, чтобы сказать «Обновить пользовательский интерфейс сейчас»? Я думаю, что это будет что-то вроде DoEvents. Попытка сохранить вопрос немного общим, чтобы он мог помочь кому-то еще когда-нибудь.
Вещи, которые я пробовал...
Я упростил свою командную функцию, просто чтобы посмотреть, смогу ли я заставить обновления ProgressText работать. Ничего из этого не работает. После выполнения всей функции отображается только последний текст, то есть Progress Update 5. Я хотел увидеть, как он проходит через каждый текст обновления по очереди.
Private Sub MoveOrRenameSongs()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 1"
Application.Current.MainWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 2"
Application.Current.MainWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 3"
Application.Current.MainWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 4"
Application.Current.MainWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 5"
Application.Current.MainWindow.UpdateLayout()
Dim myWindow As MainWindow = Application.Current.MainWindow
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 1"
myWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 2"
myWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 3"
myWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 4"
myWindow.UpdateLayout()
System.Threading.Thread.Sleep(2000)
ProgressText = "Progress update 5"
myWindow.UpdateLayout()
End Sub
Подробнее Предыстория и подробности...
У меня есть команда, которая может выполняться немного дольше, и я хотел бы обновлять текст выполнения по мере ее выполнения. Однако, поскольку командная функция выполняется в потоке пользовательского интерфейса, она блокирует пользовательский интерфейс, и текст хода выполнения в пользовательском интерфейсе не может обновляться. Я понимаю.
ProgressText привязан к свойству зависимостей, которое отлично работает в других командных функциях, которые запускаются мгновенно.
<StatusBar DockPanel.Dock = "Bottom" Height = "26">
<StatusBarItem HorizontalAlignment = "Left" >
<TextBlock Text = "{Binding StatusText}" Width = "Auto"></TextBlock>
</StatusBarItem>
<StatusBarItem HorizontalContentAlignment = "Stretch">
<TextBlock Text = "{Binding ProgressText}"></TextBlock>
</StatusBarItem>
</StatusBar>
Private _progressText As String
Public Property ProgressText() As String
Get
Return _progressText
End Get
Set(ByVal value As String)
_progressText = value
RaisePropertyChanged("ProgressText")
End Set
End Property
Я попытался перевести обработку в фоновый поток, но потом столкнулся с другими проблемами. Я перечислю несколько здесь, но они не относятся к делу.
В результате я хотел бы изучить возможность оставаться в потоке пользовательского интерфейса. Это личное приложение, которое я пишу в качестве хобби. Меня не волнует, если пользовательский интерфейс немного заблокирован, но я хотел бы видеть прогресс, когда он работает.
Что получилось в итоге
Каждый из 5 текстов прогресса появляется последовательно, как и ожидалось.
Private Async Function MoveOrRenameSongs() As System.Threading.Tasks.Task
System.Threading.Thread.Sleep(1000)
ProgressText = "Progress update 1"
Await System.Threading.Tasks.Task.Delay(100)
System.Threading.Thread.Sleep(1000)
ProgressText = "Progress update 2"
Await System.Threading.Tasks.Task.Delay(100)
System.Threading.Thread.Sleep(1000)
ProgressText = "Progress update 3"
Await System.Threading.Tasks.Task.Delay(100)
System.Threading.Thread.Sleep(1000)
ProgressText = "Progress update 4"
Await System.Threading.Tasks.Task.Delay(100)
System.Threading.Thread.Sleep(1000)
ProgressText = "Progress update 5"
Await System.Threading.Tasks.Task.Delay(100)
End Function
Я бы просто сделал команду асинхронной. Используйте await.task.delay(100) там, где у вас будут события. Это немного освобождает поток пользовательского интерфейса, и он должен иметь возможность обновлять пользовательский интерфейс. Если бы это было коммерческое приложение, я бы подумал, почему мне нужно показывать какой-то пользовательский интерфейс в середине обработки, а не до нее.
Вы можете просто ненадолго освободить поток пользовательского интерфейса и дать ему немного времени, чтобы что-то сделать.
Сделайте вашу команду асинхронной. Насколько это просто, зависит от того, как вы выполняете команды.
С помощью набора инструментов сообщества mvvm вы можете создать команду relay с асинхронной задачей:
[RelayCommand]
private async Task SaveTransaction()
{
// Expensive code
await Task.Delay(100);
// Expensive code
}
Ожидание того, что task.delay скомпилируется в таймер, и ваш код будет разделен, поэтому он возобновится через 100 мс. Он ненадолго приостановится, НО не заблокирует поток пользовательского интерфейса, как это сделал бы thread.sleep. Все, что накопилось в очереди диспетчера, начнет обрабатываться. Затем код возобновляется.
Если у вас нет такой реализации асинхронной команды, вы можете рассмотреть:
https://johnthiriet.com/mvvm-going-async-with-async-command/
большое спасибо! Это сработало, как и ожидалось. В моем решении есть реализация DelegateCommand, поэтому оно работает даже без атрибута [RelayCommand]
. Кроме того, я хотел установить статус «Переименование песни (песен) ...», а затем открыть диалоговое окно. Но я буду экспериментировать с другим порядком. Вы добавили новый инструмент в мои параметры потоковой передачи.
Вы можете попробовать вызвать
UpdateLayout
родительский элемент объекта, который хотите обновить. stackoverflow.com/questions/72407023/…