提问人:Scott 提问时间:4/14/2023 最后编辑:Scott 更新时间:4/14/2023 访问量:64
在 Invoke() 中运行时,跨线程操作在窗体控件上无效 - .NET
Cross-thread operation not valid on form control when running inside Invoke() - .NET
问:
我正在使用 VB.NET 4.8.1 我创建了一个函数 InvokeIfNecessary(action),它检查它是否在 UI 线程上运行,如果没有,则调用主窗体。
Public Sub InvokeIfNecessary(act As Action)
If IsUIThread() Then
act()
Else
Application.OpenForms(0).Invoke(act)
End If
End Sub
因为它效果非常好,所以我开始替换我的烂摊子。InvokeRequired 和其他检查与这个简单函数。但是,我只是收到错误“跨线程操作无效:从创建它的线程以外的线程访问”控制'PB_ItemImage'。
它在下面的代码中失败了_img。Visible=false。我之前暂停了执行,以确认它正在 UI 线程中运行。该变量引用了我在设计器中创建的 PictureBox,因此显然它也应该位于 UI 线程中。(我还确认该函数未在 UI 线程上,因此它正在使用 Invoke 函数。
Public Function ImgDisplayFromFile(ByRef img As PictureBox, imgFileName As String, Optional safe As Boolean = False)
Dim _img = img
If Not _img.IsDisposed Then InvokeIfNecessary(Sub()
MsgBox(IsUIThread()) 'This confirmed True
MsgBox(_img.InvokeRequired()) 'This is also true??
_img.Visible = False
DoOtherStuff()
End Sub)
End Function
这在其他地方已经奏效了。导致此错误的原因可能是什么?有什么理由不应该在OpenForms(0)上调用吗?为什么会_img。InvokeRequired 在 Invoke 函数中是否为 true?
这是作为 Form.Load 事件的一部分在下载图像文件的 Task 中执行的。
添加 IsUIThread(),在假设所有 UI 控件都位于同一线程上的情况下检查主窗体是否需要调用:
Public Function IsUIThread() As Boolean
If Application.OpenForms.Count = 0 Then Return Nothing
Return Not Application.OpenForms(0).InvokeRequired
End Function
答:
2赞
jmcilhinney
4/14/2023
#1
下面是一个示例,说明如何编写扩展方法来执行您尝试执行的操作:
Imports System.Runtime.CompilerServices
Public Module ControlExtensions
<Extension>
Public Sub InvokeIfRequired(source As Control, method As Action)
If source.InvokeRequired Then
source.Invoke(method)
Else
method()
End If
End Sub
End Module
然后,您可以像这样使用它:
Public Sub ImgDisplayFromFile(img As PictureBox, imgFileName As String, Optional safe As Boolean = False)
Dim _img = img
If Not _img.IsDisposed Then _img.InvokeIfRequired(Sub()
_img.Visible = False
DoOtherStuff()
End Sub)
End Sub
评论
1赞
Scott
4/14/2023
我喜欢这个解决方案,谢谢,虽然我有点失望,因为我认为我有办法在需要时在一个方法中安全地访问多个控件,但似乎示例中的方法应该只与控件源交互
0赞
jmcilhinney
4/14/2023
@Scott,不可以,此解决方案适用于任意数量的控件。该控件仅专门用于其 和 成员。可以访问任意数量的控件。人们会认为这些控件通常与控件处于同一形式。即使它们不是,只要它们在同一线程上创建,它仍然可以工作。如果控件位于不同线程中创建的不同窗体上,您仍然会遇到问题,但这应该是一件非常罕见的事情。source
InvokeRequired
Invoke
method
source
评论
IsUIThread
_img.InvokeRequired()
true
_img
InvokeRequired
Invoke
InvokeIfRequired
IsUIThread
PictureBox