I'm working in VB.NET 4.8.1 I created a function InvokeIfNecessary(action) which checks if it's running on the UI thread and invokes to the main form if not.
Public Sub InvokeIfNecessary(act As Action)
If IsUIThread() Then
act()
Else
Application.OpenForms(0).Invoke(act)
End If
End Sub
Because it's worked really well, I've started replacing my mess of .InvokeRequired and other checks with this simple function. However I just got the error "Cross-thread operation not valid: Control 'PB_ItemImage' accessed from a thread other than the thread it was created on."
It's failing in the code below on _img.Visible=false. I've paused execution right before to confirm it was running in the UI thread. The variable refers to a PictureBox which I created in the designer, so it should also be in the UI thread, obviously. (I also confirmed that the function had not been on the UI Thread, and so it is using the Invoke function.)
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
This has been working elsewhere. What could be different to cause this error? Is there some reason why I shouldn't invoke on OpenForms(0)? Why would _img.InvokeRequired be true inside an Invoke function?
This is executing as part of a Form.Load event, inside a Task that is downloading the image file.
Ed. to add IsUIThread() which checks if the main form requires invoking under the assumption that all UI controls are on the same thread:
Public Function IsUIThread() As Boolean
If Application.OpenForms.Count = 0 Then Return Nothing
Return Not Application.OpenForms(0).InvokeRequired
End Function
Here is an example of how you might write an extension method to do what you're trying to do:
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
You would then use that like this:
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