在打开表单之前使用 Invoke 时出现 InvalidOperationException
InvalidOperationException when using Invoke before the form was opened
例外情况是:
System.InvalidOperationException: Invoke or BeginInvoke cannot be
called on a control until the window handle has been created.
首先我将解释我的应用程序中的关系。
有一个名为 MainForm 的窗体和另一个名为 AssetsForm 的窗体。 MainForm 正在 MainForm 的构造函数中创建 AssetsForm 的实例,但尚未 AssetsForm.Show() 它。
有一个名为 AssetsSource 的 class,它实现了 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);
}
}
这是错误的,因为我应该写 action() 而不是 Invoke(action);但我稍后会提到这一点。无论如何,从 Invoke(action) 代码行抛出 InvalidOperationException。我可以推断 InvokeRequired 评估为 FALSE,尽管我从 BackgroundWorker 更新了 TreeView !!
在 MSDN 中写的是 Control.Invoke:
The Invoke method searches up the control's parent chain until it
finds a control or form that has a window handle if the current
control's underlying window handle does not exist yet. If no
appropriate handle can be found, the Invoke method will throw an
exception.
什么是父链,什么是 window 句柄? window 句柄何时创建?我想这一切都与 AssetsForm 关闭有关。
当我删除该行并仅使用 action() 时;程序不会崩溃。
当 AssetsForm 打开时 在 AssetsSource 向 AssetsForm 发送更新之前,通过调试我可以看到 InvokeRequired 被评估为 TRUE 并且 TreeView 的 BeginInvoke 更新了自己。
总而言之,我不明白为什么当 AssetsForm 关闭时,InvokeRequired 为假并且允许 UI 更新(TreeView)来自未创建的线程树视图。
只要不显示window,Winforms就不需要坚持UI-thread机制。因此 InvokeRequired
returns 为假。
如果您调用 Show()
,window 将打开,所有 UI 活动都需要 运行 通过事件循环,因此需要通过 UI-thread .
背景:只能通过主线程处理UI活动的限制是因为只有一个(windows)事件循环是处理所有 UI 相关活动。为确保所有活动 运行 以正确的顺序进行,所有操作都需要 运行 通过一个线程(至少在 winforms 中)。只要表单未显示,就不会触发任何事件,因此无需强制所有操作都运行 通过主线程。
编辑:添加一些背景描述。
例外情况是:
System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
首先我将解释我的应用程序中的关系。 有一个名为 MainForm 的窗体和另一个名为 AssetsForm 的窗体。 MainForm 正在 MainForm 的构造函数中创建 AssetsForm 的实例,但尚未 AssetsForm.Show() 它。
有一个名为 AssetsSource 的 class,它实现了 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);
}
}
这是错误的,因为我应该写 action() 而不是 Invoke(action);但我稍后会提到这一点。无论如何,从 Invoke(action) 代码行抛出 InvalidOperationException。我可以推断 InvokeRequired 评估为 FALSE,尽管我从 BackgroundWorker 更新了 TreeView !!
在 MSDN 中写的是 Control.Invoke:
The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet. If no appropriate handle can be found, the Invoke method will throw an exception.
什么是父链,什么是 window 句柄? window 句柄何时创建?我想这一切都与 AssetsForm 关闭有关。
当我删除该行并仅使用 action() 时;程序不会崩溃。
当 AssetsForm 打开时 在 AssetsSource 向 AssetsForm 发送更新之前,通过调试我可以看到 InvokeRequired 被评估为 TRUE 并且 TreeView 的 BeginInvoke 更新了自己。
总而言之,我不明白为什么当 AssetsForm 关闭时,InvokeRequired 为假并且允许 UI 更新(TreeView)来自未创建的线程树视图。
只要不显示window,Winforms就不需要坚持UI-thread机制。因此 InvokeRequired
returns 为假。
如果您调用 Show()
,window 将打开,所有 UI 活动都需要 运行 通过事件循环,因此需要通过 UI-thread .
背景:只能通过主线程处理UI活动的限制是因为只有一个(windows)事件循环是处理所有 UI 相关活动。为确保所有活动 运行 以正确的顺序进行,所有操作都需要 运行 通过一个线程(至少在 winforms 中)。只要表单未显示,就不会触发任何事件,因此无需强制所有操作都运行 通过主线程。
编辑:添加一些背景描述。