It has been like never since I worked on Winform applications. My specialty has been working with Asp.net/Websites. Recently, I was given a application to upgrade from .Net 1.1 to .Net 4.6. The application is a MDI app in which it is having issues with a Cross-thread operations. Specifically, when a button event within a user-control is clicked the intent is to show the Main MDIchild form (cfrmOverview), however an error occurs because accessed to a Picturebox control called picDisplay is prevented. Even with the added code, I am still getting the error. I opted out of using CheckForIllegalCrossThreadCalls because it affects other parts the program and MSDN suggest not too. Insight needed.
Public Delegate Sub Mydelegate(ByVal AControl As PictureBox)
Public Shared Sub CreateEnableControl(ByVal AControl As PictureBox)
AControl.Visible = True
AControl.Enabled = True
End Sub
Public Shared Sub NavigateTo(ByVal sender As System.Windows.Forms.UserControl, ByVal aNavTarget As String, Optional ByVal param As Object = Nothing)
Dim aType As Type
Dim Types() As Type
Dim aobject As Object
Try
If IsNothing(System.Reflection.Assembly.GetEntryAssembly) Then
aobject = sender.ParentForm
Types = System.Reflection.Assembly.GetAssembly(aobject.GetType).GetTypes
Else
Types = System.Reflection.Assembly.GetEntryAssembly.GetTypes
End If
Dim aForm As Windows.Forms.Form
For Each aType In Types
If aType.BaseType Is GetType(MdiChild) Then
If aType.Name = aNavTarget Then
Dim aMdiParent As Windows.Forms.Form
If TypeOf (sender.ParentForm) Is MdiParent Then
aMdiParent = sender.ParentForm
Else
aMdiParent = sender.ParentForm.ParentForm
End If
For Each aForm In aMdiParent.MdiChildren
If aType.FullName Is aForm.GetType.FullName Then
aForm.Tag = param
'Added Code below to try to prevent Cross-Thread exception on PicDisplay found in the Main cfrmOverview Form
'that has designed time user control embedded.
'New Code Start----------------------------------------------------------------------
For Each aControl As Windows.Forms.Control In aForm.Controls.Find("picDisplay", True)
If aControl.InvokeRequired Then
Dim myArray(0) As Object
myArray(0) = New PictureBox
aControl.BeginInvoke(New Mydelegate(AddressOf CreateEnableControl), myArray)
End If
Next
'New Code End------------------------------------------------------------------------
aForm.Show() 'Cross-thread exception for picDisplay is here.
GoTo Success
End If
You should/must1 Invoke code which accesses a control on the thread on which the control was created.
Don't mix up Control.Invoke and delegate.BeginInvoke. See What's the difference between Invoke() and BeginInvoke()
The line which raises the error could be changed to
aForm.Invoke(Sub() aForm.Show())
However, you might not always want to invoke, for instance when you are executing code on the proper thread already, the Invoke call is wasteful. That's why Controls have the InvokeRequired
property. Here is a specific implementation of the invoke-if-invokerequired pattern for showing a form.
Private Sub showForm(ByVal f As Form)
If f.InvokeRequired Then
f.Invoke(New Action(Of Form)(AddressOf showForm), f)
Else
f.Show()
End If
End Sub
' usage:
showForm(aForm)
If you are doing a lot of UI programming, you may find that you are writing these methods a lot. So you can automate the pattern
You can put this extension method in a module. It allows you to pass a delegate which does whatever you want, and it will be invoked on the control's thread if required
<Extension()> _
Public Sub InvokeIfRequired(ByVal control As Control, action As MethodInvoker)
If control.InvokeRequired Then
control.Invoke(action)
Else
action()
End If
End Sub
' usage:
aForm.InvokeIfRequired(Sub() aForm.Show())
1 In some cases, accessing controls from the wrong thread will not raise an exception, but it can cause intermittent exceptions. In my experience it is non-deterministic. For example, retrieving TextBox.Text
on the wrong thread is usually fine, but setting TextBox.Text
will usually raise an exception. For this reason, it is good practice to use the invoke-if-invokerequired pattern whenever doing anything with controls outside of their own event handlers, or at least outside event handlers for controls on the same form.