例外是:
System.InvalidOperationException:Invoke或BeginInvoke不能 调用控件直到创建窗口句柄。
首先,我将解释我的应用中的关系。 有一个名为MainForm的表单和另一个名为AssetsForm的表单。 MainForm正在MainForm的constrcutor中创建AssetsForm的一个实例,但它还没有AssetsForm.Show()。
有一个名为AssetsSource的类,它实现IObservable并将数据发送到实现IObserver的AssetsForm。当AssetsForm接收要显示的数据时,它会创建一个BackgroundWorker来处理数据并更新TreeView。
我已经实施了以下错误的代码来处理来自BackgroundWorker的UI更新:
private void Invoke(Control control, Action action)
{
if (control.InvokeRequired)
{
control.BeginInvoke(action);
}
else
{
control.Invoke(action);
}
}
这是错的,因为我不应该使用Invoke(action)来写动作();但我稍后会提到这一点。无论如何,从Invoke(action)代码行抛出了InvalidOperationException。虽然我从BackgroundWorker更新了TreeView,但我可以推断InvokeRequired的计算结果为FALSE !!
在MSDN中,它是关于Control.Invoke:
的Invoke方法搜索控件的父链,直到它为止 找到一个控件或窗体,如果是当前窗口句柄 控件的底层窗口句柄尚不存在。如果不 可以找到合适的句柄,Invoke方法会抛出一个 异常。
什么是父链,什么是窗口句柄?创建窗口句柄时?我想这一切都与AssetsForm关闭的事实有关。
当我删除该行并仅使用action()时;应该是,程序不会崩溃。
在AssetsSource向AssetsForm发送更新之前 AssetsForm打开时,通过调试我可以看到InvokeRequired被评估为TRUE并且TreeView的BeginInvoke自行更新。
总结一下,我不明白为什么当AssetsForm关闭时,InvokeRequired为false,允许UI更新(TreeView)来自没有创建TreeView的线程
答案 0 :(得分:0)
只要窗口没有显示,Winforms就不需要坚持使用UI线程机制了。因此InvokeRequired
返回false。
如果您调用Show()
,则会打开窗口,并且所有UI活动都需要通过事件循环运行,因此需要通过UI线程运行。
背景:仅通过主线程处理UI活动的限制是由于只有一个(windows)事件循环正在处理所有与UI相关的活动。为了确保所有活动都以正确的顺序运行,所有操作都需要通过一个线程运行(至少在winforms中)。只要未显示表单,就不会触发任何事件,因此无需强制所有操作都通过主线程运行。
编辑:添加一些背景说明。