我理解事件处理程序在调用事件的任何线程上执行。我进一步理解只需要从创建控件的线程更新表单控件。我假设UI线程是为此问题创建表单的那个。
如果事件是发布消息的结果,例如绘制消息,那么处理程序是否与原始线程分离?如果这是真的,那么任何线程都可以调用失效操作,并且结果绘制将始终出现在UI线程上,因为它是处理表单消息的那个。
这就是它在凌晨2点左右在我脑海中映射出来的方式,我身边有一个长长的空小吃碗。请澄清并纠正,以便我能够正确理解工作中的机制。
答案 0 :(得分:5)
MSDN:
调用Invalidate方法不会强制执行同步绘制;要强制执行同步绘制,请在调用Invalidate方法后调用Update方法。
因此,可以从任何线程调用Invalidate,并且仅从UI线程调用。在任何情况下,要100%确定不使用无效的跨线程调用,请在程序开头将Control :: CheckForIllegalCrossThreadCalls属性设置为true。这会导致任何无效调用立即失败,您无需猜测。
答案 1 :(得分:1)
首先,请参阅亚历克斯的答案。
其次,请注意,仅仅因为你做了一些事情导致消息泵中的某些内容结束,从而导致UI线程上的事件,并不绝对意味着启动操作必须在同一个线程上发生。
我在这里讲得非常广泛;但请注意,任何代码都可以通过调用Control.Invoke()或等效函数在内部封送到UI线程。
在您自己的应用程序代码中,您必须确保这样做。但我只是建议如果你在某个地方找到似乎违反了这个原则的代码,那么可能会在某处为你调用Invoke()。
答案 2 :(得分: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);
}
}
这里有一些问题:
如果在Form
检查完成后处理IsHandleCreated
,则句柄将消失;
将在调用线程上调用调用NotifyInvalidate
来触发OnInvalidated
事件的Invalidated
。注册到该事件的处理程序可能不会期望从其他线程调用它;
尽管这表明可以从单独的线程调用InvalidateRect
,但它并未说明RedrawWindow
是否可以。因此,问题就变成了您是拨打Invalidate(false)
(或Invalidate()
,这是否映射到false
),还是致电Invalidate(true)
。
长话短说。你不应该从另一个线程调用它。您应该通过Control
或Form
调用与Invoke
(以及BeginInvoke
)互动的所有方法。