我有一个带有自定义控件的表单。
我的表单中有一个方法:
private void SetEnabledOnControls(bool val)
{
if (InvokeRequired)
{
Invoke((Action<bool>)SetEnabledOnControls, val);
}
else
{
//do the work - iterate over child controls,
//and they iterate over their children, etc...
}
}
在
else
分支中的方法内,我得到了上述异常:Cross-thread operation not valid: Control 'txtNumber' accessed from a thread other than the thread it was created on.
我的情境实际上有点复杂——我只是以此作为例子。实际上正在发生的是,我正在使用WorkflowFoundation - 我有一个在WorkflowApplication(它在自己的线程中运行)中运行的StateMachineActivity(CTP1),我订阅了它的事件,并从那里调用SetEnabledOnControls
。此外,我使用书签来恢复我的工作流程(还有MEF在旁边,不涉及情境)。所有这些都与我对InvokeRequired的明显误解无关——如果InvokeRequired为false,我怎么可能会出现跨线程异常?我没有“手动”创建任何控件——它们都在由设计师放置的Initialize()中。
有人能给我一些启示吗?
谢谢!
编辑
使用GWLlosa的建议,我使用System.Threading.Thread.CurrentThread.ManagedThreadId
跟踪了线程ID。现在来到了奇怪的部分... Initialize()中的线程ID是10。在通过前两个状态后,它以ID 13进入 - InvokeRequired为true,并且正确调用了。但是,在第二个状态之后,当它进入SetEnabledOnControls
时,它再次变成了13,但这次InvokeRequired为false!怎么回事?当然,后来它无法更改子控件(不足为奇)。难道窗体在某种情况下改变了它所在的线程吗?
编辑2 现在我正在调用:
if (IsHandleCreated)
{
Invoke((Action<bool>)SetEnabledOnControls, val);
}
它将IsHandleCreated
设置为true,但仍然在devSpeed 指向的问题上失败。
编辑3 FACEPALM :) 其中一个按钮最初是Form的CancelButton。当它从属性中被移除后,代码仍然具有DialogResult=Cancel - 因此我的窗体确实关闭了,当然它缺少句柄,所以InvokeRequired没有返回正确的信息,因此出现了错误。
谢谢大家!我今天学到了新东西 :)