在打开表单之前使用Invoke时出现InvalidOperationException

时间:2016-01-23 17:32:35

标签: c# multithreading winforms invoke

例外是:

  

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的线程

1 个答案:

答案 0 :(得分:0)

只要窗口没有显示,Winforms就不需要坚持使用UI线程机制了。因此InvokeRequired返回false。

如果您调用Show(),则会打开窗口,并且所有UI活动都需要通过事件循环运行,因此需要通过UI线程运行。

背景:仅通过主线程处理UI活动的限制是由于只有一个(windows)事件循环正在处理所有与UI相关的活动。为了确保所有活动都以正确的顺序运行,所有操作都需要通过一个线程运行(至少在winforms中)。只要未显示表单,就不会触发任何事件,因此无需强制所有操作都通过主线程运行。

编辑:添加一些背景说明。