2010-10-30 54 views
4

我知道一个事件处理程序在任何调用该事件的线程上执行。我进一步理解需要仅从创建控件的线程更新表单控件。我假设UI线程是为这个问题创建表单的人。.NET事件,线程和消息

如果事件是发布消息的结果,例如绘制消息,则不是该处理程序,那么就从原始线程中解耦?如果这是真的,那么任何线程都可以调用无效操作,并且生成的绘图将始终发生在UI线程上,因为它是处理表单消息的一个线程。

这是如何在我的脑海中在凌晨2点左右映射出我身边长长的空小吃碗。请澄清并纠正,以便我可以正确理解工作中的机制。

回答

5

MSDN:

调用Invalidate方法不强制同步漆;要强制同步绘画,请在调用Invalidate方法后调用Update方法。

所以,可以从任何线程调用Invalidate,并且仅从UI线程更新。在任何情况下,要100%确定您不使用无效的跨线程调用,请在程序的开头将Control :: CheckForIllegalCrossThreadCalls属性设置为true。这会导致任何无效呼叫立即失败,您不需要猜测。

+0

在Invalidate之后调用'Update'与调用'Refresh'相同,这将导致必须从UI线程调用。 – 2013-08-26 14:06:23

1

首先,请看Alex的回答。第二,请注意,仅仅因为你做了一些事情导致消息泵中的事件结束,从而在UI线程上产生事件,并不意味着启动动作必须发生在同一个线程上。

我在这里讲得很宽泛;但请注意,任何代码都可以通过调用Control.Invoke()或等价物将内容封送到UI线程。

在您自己的应用程序代码中,当然您必须确保这样做。但我只是建议,如果您在某处看到违反此原则的代码,则可能是Invoke()在某处为您服务。

0

即使在技术上可能做到这一点,您也会遇到麻烦。为Invalidate的代码如下:

public void Invalidate(bool invalidateChildren) { 
    if (IsHandleCreated) { 
     if (invalidateChildren) { 
      SafeNativeMethods.RedrawWindow(new HandleRef(window, Handle), 
              null, NativeMethods.NullHandleRef, 
              NativeMethods.RDW_INVALIDATE | 
              NativeMethods.RDW_ERASE | 
              NativeMethods.RDW_ALLCHILDREN); 
     } 
     else { 
      // It's safe to invoke InvalidateRect from a separate thread. 
      using (new MultithreadSafeCallScope()) 
      { 
       SafeNativeMethods.InvalidateRect(new HandleRef(window, Handle), 
               null, 
               (controlStyle & ControlStyles.Opaque) != ControlStyles.Opaque); 
      } 
     } 

     NotifyInvalidate(this.ClientRectangle); 
    } 
} 

有几个问题在这里:

  1. 如果Form设置在IsHandleCreated检查完成后,手柄会消失;

  2. 调用OnInvalidatedNotifyInvalidate将触发调用线程调用Invalidated事件。注册到该事件的处理程序可能不希望它从另一个线程被调用;

  3. 即使这说明InvalidateRect可以从一个单独的线程调用,但它并没有说明RedrawWindow是否可以。问题因此变成是否您致电Invalidate(false)(或Invalidate(),这将映射到false)或您打电话Invalidate(true)

长话短说。你不应该从另一个线程调用它。你应该调用所有与Control(也是Form)到InvokeBeginInvoke交互的方法。