我知道一个事件处理程序在任何调用该事件的线程上执行。我进一步理解需要仅从创建控件的线程更新表单控件。我假设UI线程是为这个问题创建表单的人。.NET事件,线程和消息
如果事件是发布消息的结果,例如绘制消息,则不是该处理程序,那么就从原始线程中解耦?如果这是真的,那么任何线程都可以调用无效操作,并且生成的绘图将始终发生在UI线程上,因为它是处理表单消息的一个线程。
这是如何在我的脑海中在凌晨2点左右映射出我身边长长的空小吃碗。请澄清并纠正,以便我可以正确理解工作中的机制。
我知道一个事件处理程序在任何调用该事件的线程上执行。我进一步理解需要仅从创建控件的线程更新表单控件。我假设UI线程是为这个问题创建表单的人。.NET事件,线程和消息
如果事件是发布消息的结果,例如绘制消息,则不是该处理程序,那么就从原始线程中解耦?如果这是真的,那么任何线程都可以调用无效操作,并且生成的绘图将始终发生在UI线程上,因为它是处理表单消息的一个线程。
这是如何在我的脑海中在凌晨2点左右映射出我身边长长的空小吃碗。请澄清并纠正,以便我可以正确理解工作中的机制。
MSDN:
调用Invalidate方法不强制同步漆;要强制同步绘画,请在调用Invalidate方法后调用Update方法。
所以,可以从任何线程调用Invalidate,并且仅从UI线程更新。在任何情况下,要100%确定您不使用无效的跨线程调用,请在程序的开头将Control :: CheckForIllegalCrossThreadCalls属性设置为true。这会导致任何无效呼叫立即失败,您不需要猜测。
首先,请看Alex的回答。第二,请注意,仅仅因为你做了一些事情导致消息泵中的事件结束,从而在UI线程上产生事件,并不意味着启动动作必须发生在同一个线程上。
我在这里讲得很宽泛;但请注意,任何代码都可以通过调用Control.Invoke()或等价物将内容封送到UI线程。
在您自己的应用程序代码中,当然您必须确保这样做。但我只是建议,如果您在某处看到违反此原则的代码,则可能是Invoke()在某处为您服务。
即使在技术上可能做到这一点,您也会遇到麻烦。为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);
}
}
有几个问题在这里:
如果Form
设置在IsHandleCreated
检查完成后,手柄会消失;
调用OnInvalidated
的NotifyInvalidate
将触发调用线程调用Invalidated
事件。注册到该事件的处理程序可能不希望它从另一个线程被调用;
即使这说明InvalidateRect
可以从一个单独的线程调用,但它并没有说明RedrawWindow
是否可以。问题因此变成是否您致电Invalidate(false)
(或Invalidate()
,这将映射到false
)或您打电话Invalidate(true)
。
长话短说。你不应该从另一个线程调用它。你应该调用所有与Control
(也是Form
)到Invoke
或BeginInvoke
交互的方法。
在Invalidate之后调用'Update'与调用'Refresh'相同,这将导致必须从UI线程调用。 – 2013-08-26 14:06:23