MDI跨线程异常在父和子之间通过按钮usercontrol .net 1.1到4.6



这是我从事Winform应用程序以来从未有过的。我的专长是与Asp.net/Websites合作。最近,我得到了一个从。net 1.1升级到。net 4.6的应用程序。该应用程序是一个MDI应用程序,它在跨线程操作中有问题。具体来说,当用户控件中的按钮事件被单击时,意图是显示主mdicchild窗体(cfrmOverview),但是错误发生了,因为访问名为picDisplay的Picturebox控件被阻止了。即使添加了代码,我仍然得到错误。我选择不使用CheckForIllegalCrossThreadCalls,因为它会影响程序的其他部分,MSDN建议不要使用。洞察力。

    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

你应该/必须1调用在创建控件的线程上访问控件的代码

不要混淆Control。调用和委派。参见Invoke()和BeginInvoke()的区别

引发错误的行可以改为

aForm.Invoke(Sub() aForm.Show())
但是,您可能并不总是希望调用,例如,当您已经在正确的线程上执行代码时,调用invoke是浪费的。这就是为什么控件有InvokeRequired属性。下面是用于显示表单的invoke-if- invokerrequired模式的具体实现。
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)

如果你做了很多UI编程,你可能会发现你写了很多这些方法。所以你可以自动生成模式

你可以把这个扩展方法放在一个模块中。它允许你传递一个委托,它可以做任何你想要的,如果需要,它将在控件的线程上被调用

<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在某些情况下,从错误的线程访问控件不会引发异常,但可能会导致间歇性异常。根据我的经验,这是不确定的。例如,在错误的线程上检索TextBox.Text通常没有问题,但是设置TextBox.Text通常会引发异常。由于这个原因,使用invoke-if- invokerrequired模式是一种很好的做法,每当对控件自己的事件处理程序之外的控件做任何事情时,或者至少在同一表单上的控件的事件处理程序之外。

最新更新