Как выполнить код в потоке GUI? - PullRequest
5 голосов
/ 15 сентября 2010

У меня есть FileSystemWatcher, который реагирует на событие Changed.

Я хочу открыть файл, прочитать его содержимое, отобразить его в текстовом поле и скрыть всплывающее окно, созданное через 1 сек.Код почти работает, но что-то не работает при скрытии всплывающего окна.

Вот фрагмент кода:

       txtLog.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() {
            this.txtLog.Text = dataToDisplay;
            extendedNotifyIcon_OnShowWindow();
            Thread threadToClosePopup = new Thread(new ThreadStart((Action)delegate() { 
                Thread.Sleep(1000); 

                extendedNotifyIcon_OnHideWindow();
       }));
            threadToClosePopup.Start();
        });

Как видите, я использую Invoke для установки текста, потому чтособытие находится в другом потоке (FileSystemWatcher).Но чтобы скрыть окна, extendedNotifyIcon_OnHideWindow () не выполняется в потоке графического интерфейса.Как я могу выполнить это в потоке GUI?

Ответы [ 6 ]

19 голосов
/ 26 мая 2016

Это хорошо работает для WPF с MVVM.

Application.Current.Dispatcher.Invoke(
    () =>
    {
         // Code to run on the GUI thread.
    });

Это будет не работать последовательно (произойдет сбой, если мы находимся внутри обработчика для Reactive Extensions):

Dispatcher.CurrentDispatcher.Invoke(
    () =>
    {
         // Fails if we are inside a handler for Reactive Extensions!
    });

Дополнительно для экспертов: причина?

По своей конструкции любой поток может иметь сопряженный с ним поток диспетчера, см. MSDN: Класс диспетчера .

Если мы ссылаемся на Dispatcher.CurrentDispatcher из какого-либо потока, он фактически создаст новый поток диспетчера, который отделен от официального потока диспетчера пользовательского интерфейса WPF.Когда мы пытаемся выполнить код в этом недавно созданном потоке диспетчера, WPF выдает его, поскольку он не является официальным потоком диспетчера пользовательского интерфейса.

Решение состоит в том, чтобы всегда использовать Application.Current.Dispatcher.Invoke() или Application.Current.Dispatcher.BeginInvoke().См. В чем разница между Invoke () и BeginInvoke () .


Проверено на:

  • WPF
  • .NET 4.5
  • .NET 4.6
  • .NET 4.61
10 голосов
/ 15 сентября 2010

Чтобы выполнить метод extendedNotifyIcon_OnHideWindow в потоке GUI, используйте Dispatcher, как вы это сделали, чтобы показать его.

Thread threadToClosePopup = new Thread(new ThreadStart((Action)delegate() { 
  Thread.Sleep(1000); 
  txtLog.Dispatcher.Invoke(
    DispatcherPriority.Normal,
    (Action)() => extendedNotifyIcon_OnHideWindow());
}));
2 голосов
/ 15 сентября 2010

Использовать Control.Invoke

   txtLog.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() {
        this.txtLog.Text = dataToDisplay;
        extendedNotifyIcon_OnShowWindow();
        Thread threadToClosePopup = new Thread(new ThreadStart((Action)delegate() { 
            Thread.Sleep(1000); 

            extendedNotifyIcon.Invoke(extendedNotifyIcon_OnHideWindow);
   }));
        threadToClosePopup.Start();
    });
2 голосов
/ 15 сентября 2010

Это даст вам диспетчер окон:

Dispatcher.CurrentDispatcher

Пока вы получаете это в потоке Windows.

1 голос
/ 15 сентября 2010

Просто заверните extendedNotifyIcon_OnHideWindow(); в Dispatcher.Invoke()

Но я бы предпочел сделать это (все в XAML), используя анимацию и EvenTrigger, который срабатывает при событии TimeLine.Completed.

0 голосов
/ 15 сентября 2010

Проблема в том, что extendedNotifyIcon_OnHideWindow выполняется в потоке, отличном от потока пользовательского интерфейса.Вам также понадобится использовать Dispatcher для этой части.Кроме того, я бы не создавал отдельную тему, чтобы просто подождать одну секунду.Вы можете легко преобразовать эту часть в System.Threading.Timer.Вот моя версия с рефакторингом.

txtLog.Dispatcher.Invoke(
    (Action)(() =>
        {
            txtLog.Text = dataToDisplay;
            extendedNotifyIcon_OnShowWindow(); 
            new Timer(
                (state) =>
                {
                    button1.Dispatcher.BeginInvoke(
                        (Action)(() =>
                            {
                              extendedNotifyIcon_OnHideWindow(); 
                            }), null);
                }, null, 1000, Timeout.Infinite);
        }));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...