Немедленно обновить связанное свойство из потока пользовательского интерфейса

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

В моем случае я хочу наоборот. Есть ли что-нибудь, что я могу вызвать или сделать в команде, работающей в потоке пользовательского интерфейса, чтобы сказать «Обновить пользовательский интерфейс сейчас»? Я думаю, что это будет что-то вроде 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

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

  • Я могу вызвать Dispatcher.Invoke для обновления ProgressText, НО
  • Я больше не могу открыть окно, которое мне нужно для ввода данных; поток должен быть STA
  • Я разобрался, как сделать поток STA, тогда я не могу получить доступ к родителю, чтобы установить положение окна ввода
  • обойти все это, и мой связанный список больше не обновляется в пользовательском интерфейсе, когда я обновляю его в конце, потому что весь код написан для того, чтобы находиться в потоке пользовательского интерфейса, поэтому эти обновления не работают

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

Что получилось в итоге

Каждый из 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

Вы можете попробовать вызвать UpdateLayout родительский элемент объекта, который хотите обновить. stackoverflow.com/questions/72407023/…

Joe 17.01.2023 03:25

Я бы просто сделал команду асинхронной. Используйте await.task.delay(100) там, где у вас будут события. Это немного освобождает поток пользовательского интерфейса, и он должен иметь возможность обновлять пользовательский интерфейс. Если бы это было коммерческое приложение, я бы подумал, почему мне нужно показывать какой-то пользовательский интерфейс в середине обработки, а не до нее.

Andy 17.01.2023 15:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Сделайте вашу команду асинхронной. Насколько это просто, зависит от того, как вы выполняете команды.

С помощью набора инструментов сообщества 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]. Кроме того, я хотел установить статус «Переименование песни (песен) ...», а затем открыть диалоговое окно. Но я буду экспериментировать с другим порядком. Вы добавили новый инструмент в мои параметры потоковой передачи.

Sandra 17.01.2023 17:19

Другие вопросы по теме