2016-09-30 18 views
0

自从我工作在Winform应用程序以来一直如此。我的专业一直在与Asp.net/Websites合作。最近,我获得了一个应用程序,可以从.Net 1.1升级到.Net 4.6。该应用程序是一个MDI应用程序,它在跨线程操作时遇到问题。具体来说,当点击用户控件中的按钮事件时,意图是显示主MDIchild表单(cfrmOverview),但是由于禁止访问名为picDisplay的Picturebox控件,因此会发生错误。即使添加了代码,我仍然收到错误。我选择退出使用CheckForIllegalCrossThreadCalls,因为它会影响程序和MSDN建议的其他部分。 Insight需要。通过usercontrol .net 1.1到4.6之间的父级和子级之间的MDI跨线程异常通过按钮.net 1.1到4.6

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 
+0

您正在测试'InvokeRequired'并在该代码中调用'BeginInvoke'的事实表明它将会或可能不会在UI线程上执行。这意味着它不应该访问该代码中的任何表单或其他控件的任何成员。 'aForm'的'Show'方法是一个表单的成员,所以它是不受限制的。你需要做一个类似的测试,并调用在UI线程上调用该窗体上的“Show”。 – jmcilhinney

回答

1

你应该/必须接入其在其上创建控件的线程上控制调用代码。

不要混淆Control.Invoke和delegate.BeginInvoke。见What's the difference between Invoke() and BeginInvoke()

这引起了错误可改为

aForm.Invoke(Sub() aForm.Show()) 

然而行,你可能不总是想调用,比如当你正确的线程上执行代码已中,Invoke电话浪费。这就是为什么Controls拥有InvokeRequired属性。这是用于显示表单的invoke-if-invokerequired模式的特定实现。

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()) 

在某些情况下,从错误的线程访问控制将不会产生异常被调用,但它可能会导致间歇性例外。根据我的经验,这是非确定性的。例如,在错误的线程上检索TextBox.Text通常可以,但设置TextBox.Text通常会引发异常。由于这个原因,每当使用自己的事件处理程序之外的控件进行任何操作时,或者至少在同一表单上的控件的外部事件处理程序中使用invoke-if-invokerequired模式是一种很好的做法。

+0

我尝试了你的第一个建议,但是这消除了交叉线程异常,但是,现在MDI Child cfrmOverview需要花费大量的时间来加载。 – Blackie

+0

由于跨线程异常,它没有加载之前,正确吗?也许'cfrmOverview.cfrmOverview_Load()'只是做了很多事情。 – djv

相关问题