如何在GUI线程中执行代码?

时间:2010-09-15 14:27:28

标签: c# wpf multithreading c#-4.0

我有一个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的线程中执行。如何在GUI线程中执行它?

6 个答案:

答案 0 :(得分:17)

适用于margin的{​​{1}}。

WPF

这将一致地工作(如果我们在Reactive Extensions的处理程序中,它将会失败):

MVVM

专家额外:原因?

按照设计,任何线程都可以有一个与之配对的调度程序线程,请参阅MSDN: Dispatcher Class

如果我们从任何线程引用Application.Current.Dispatcher.Invoke( () => { // Code to run on the GUI thread. }); ,它实际上会创建一个新的调度程序线程,该线程与官方WPF UI调度程序线程分开。当我们尝试在这个新创建的调度程序线程上执行代码时,WPF将抛出,因为它不是官方的UI调度程序线程。

解决方案是始终使用Dispatcher.CurrentDispatcher.Invoke( () => { // Fails if we are inside a handler for Reactive Extensions! }); Dispatcher.CurrentDispatcher。请参阅What's the difference between Invoke() and BeginInvoke()

经过测试:

  • Application.Current.Dispatcher.Invoke()
  • Application.Current.Dispatcher.BeginInvoke()
  • WPF
  • .NET 4.5

答案 1 :(得分:10)

要在GUI线程上执行extendedNotifyIcon_OnHideWindow方法,请像使用Dispatcher一样显示它。

Thread threadToClosePopup = new Thread(new ThreadStart((Action)delegate() { 
  Thread.Sleep(1000); 
  txtLog.Dispatcher.Invoke(
    DispatcherPriority.Normal,
    (Action)() => extendedNotifyIcon_OnHideWindow());
}));

答案 2 :(得分:2)

这将为您提供Window调度程序:

Dispatcher.CurrentDispatcher

只要你在windows线程上获得它。

答案 3 :(得分:2)

使用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();
    });

答案 4 :(得分:1)

extendedNotifyIcon_OnHideWindow();包裹到Dispatcher.Invoke()

但是我宁愿使用动画和使用TimeLrigger来触发TimeLine.Completed事件来进行(所有im XAML)。

答案 5 :(得分:0)

问题是extendedNotifyIcon_OnHideWindow正在UI线程以外的线程上执行。您还需要使用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);
        }));