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

本文关键字:Invoke 时的 InvalidOperationException 表单 | 更新日期: 2023-09-27 18:31:45

例外是这样的:

System.InvalidOperationException: Invoke 或 BeginInvoke 不能 在控件上调用,直到创建了窗口句柄。

首先,我将在我的应用程序中解释关系。有一个名为 MainForm 的窗体和另一个名为 AssetsForm 的窗体。MainForm 正在 MainForm 的构造器中创建 AssetsForm 的实例,但还没有 AssetsForm.Show() 它。

有一个名为 AssetsSource 的类,它实现 IObservable,并将要显示的数据发送到实现 IObserver 的 AssetsForm。当 AssetsForm 收到要显示的数据时,它会创建一个处理数据并更新 TreeView 的 BackgroundWorker 。

我实现了以下错误的代码来处理来自后台工作者的 UI 更新:

    private void Invoke(Control control, Action action)
    {
        if (control.InvokeRequired)
        {
            control.BeginInvoke(action);
        }
        else
        {
            control.Invoke(action);
        }
    }

这是错误的,因为应该写 action() 而不是 Invoke(action),我应该写 action();但我稍后会提到这一点。无论如何,从 Invoke(action) 代码行中抛出了一个 InvalidOperationException。我可以推断调用要求评估为 FALSE,尽管我从后台工作者更新了树视图!

在MSDN中,它是关于Control.Invoke的:

Invoke 方法向上搜索控件的父链,直到它 查找具有窗口句柄的控件或窗体,如果当前 控件的基础窗口句柄尚不存在。如果没有 可以找到适当的句柄,Invoke 方法将抛出一个 例外。

什么是父链,什么是窗口句柄?何时创建窗口句柄?我想这一切都与资产表单已关闭的事实有关。

当我删除该行并仅使用action()时,程序不会崩溃。

当在AssetsSource向AssetsForm发送更新之前打开AssetsForm时,通过调试,我可以看到InvokeRequired被评估为TRUE,并且TreeView的BeginInvoke会更新自身。

总而言之,我不明白为什么当 AssetsForm 关闭时,调用必需是假的,并且允许 UI 更新(树视图)来自未创建树视图的线程。

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

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

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

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

编辑:添加一些背景描述。