Warum wird eine Wertekopie von MainForm erstellt, wenn eine Methode aufgerufen oder ein Cross-Thread aufgerufen wird?

Update: Ich denke, es hat etwas mit der verzögerten Instanziierung des Fenster-Handles für MainForm zu tun - aber ich konnte nicht genau herausfinden, wie dies zu dem hier gezeigten Verhalten führen würde.

Die Anwendung fordert Daten über die COM-Schnittstelle eines Drittanbieters an und gibt einen Rückruf zur Verarbeitung der Ergebnisse aus. Im Rückruf muss die Benutzeroberfläche aktualisiert werden - das Update funktioniert jedoch nicht wie erwartet. Es ist, als ob eine Wertekopie von MainForm erstellt worden wäre, wennMainForm.DataReady wird als Cross-Thread bezeichnet oder direkt aufgerufen, aber die Aktualisierung der Benutzeroberfläche funktioniert erwartungsgemäß, wenn sie von einem Ereignishandler ausgeführt wird. Kannst du erklären warum?

(Hinweis:AppDomain.CurrentDomain.Id ist immer1 ob in MainForm oder in ClassB geprüft.)

Anfangscode - Aufrufen von DataReady von einer ClassB-Instanz ohne InvokeRequred / Delegate / Invoke-Logik in MainForm. Die Änderung der Anwendungsbenutzeroberfläche funktioniert wie erwartet, MainFormSomeListControl.EmptyListMsg = "Not Available" Änderung bleibt nicht „hängen“ (als ob sie auf eine separate Kopie von MainForm angewendet würde)



Module AppGlobals
  Public WithEvents A As ClassA
End Module

Partial Friend Class MyApplication
  Private Sub MyApplication_Startup(ByVal sender As Object,
                                          ByVal e As StartupEventArgs) Handles Me.Startup
    A = New ClassA()

  End Sub
End Class

Class MainForm

  private sub getData
    ToggleWait(True)
    SomeListControl.Clear()
    A.getData() 'Sets up the com object & callback
  end sub

  Public Sub DataReady()
    ToggleWait(False)
    ' Do something with the data
  End Sub

  Private Sub ToggleWait(toggle as Boolean)
    Application.UseWaitCursor = False
    if toggle then
      SomeListControl.EmptyListMsg = "Not Available"
    else
      SomeListControl.EmptyListMsg = "Please Wait"
    end if
  End Sub

End Class

Class ClassA

  public sub getData()
     Dim ComObj as New ComObject
     Call ComObj.setClient(New ClassB)
  End Sub

End Class

Class ClassB
  Implements IComObjectClient

  sub getdata_callback(results() as Object) handles IComObjectClient.getdata_callback
    ' Get the results
    MainForm.DataReady() 
  end sub

End Class

Hinzufügen der InvokeRequred-Logik zu DataReady, die immer noch direkt von ClassB aufgerufen wird. InvokeRequired ist niemals wahr. Die Änderung der Anwendungsoberfläche funktioniert wie erwartet, MainFormSomeListControl.EmptyListMsg = "Not Available" Änderung bleibt nicht „hängen“ (als ob sie auf eine separate Kopie von MainForm angewendet würde)


  Class MainForm
    Public Delegate Sub DataReadyDelegate(ByVal toggle As Boolean)
    ...
    Public Sub DataReady()
        If InvokeRequired Then
            Invoke(New DataReadyDelegate()
        Else
          ToggleWait(False)
          ' Do something with the data
        End If
    End Sub
    ...
  End Class

AufgerufenMainForm.DataReady direkt von ClassB Erhaltene Ausnahme: "Invoke oder BeginInvoke können nicht für ein Steuerelement aufgerufen werden, bis das Fensterhandle erstellt wurde." bis ich die Erstellung des Fenstergriffs erzwungen habe. Dann ist es das gleiche Verhalten wie zuvor, nämlich InvokeRequired ist nie wahr, Application UI change funktioniert wie erwartet, MainFormSomeListControl.EmptyListMsg = "Not Available" Änderung bleibt nicht „hängen“ (als ob sie auf eine separate Kopie von MainForm angewendet würde)


Class ClassB
  Implements IComObjectClient
  Public Delegate Sub DataReadDelegate()

  sub getdata_callback(results() as Object) handles IComObjectClient.getdata_callback
    ' Get the results 
    If Not MainForm.IsHandleCreated Then
      ' This call forces creation of the control's handle
      Dim handle As IntPtr = MainForm.Handle
    End If
    MainForm.Invoke(New DataReadyDelegate(AddressOf MainForm.DataReady))
  end sub

End Class

Wird vom Event Handler ausgeführt Definierte benutzerdefinierte "Got Data" -Ereignisse in ClassA und ClassB. ClassA lauscht auf ClassB.got_data_event und löst ClassA.got_data_event aus, MainForm lauscht auf ClassA.got_data_event und verarbeitet dies durch Aufrufen von DataReady (). Dies funktioniert - InvokeRequired ist true, Invoke wird ausgeführt, Änderungen an der Benutzeroberfläche von Application und MainForm funktionieren wie beabsichtigt.


  Class MainForm
    Public Delegate Sub DataReadyDelegate()
    ...
    Public Sub DataReady()
        If InvokeRequired Then
            Invoke(New DataReadyDelegate()
        Else
          ToggleWait(False)
          ' Do something with the data
        End If
    End Sub

    Public Sub _GotData_HandleEvent(ByVal resultMessage As String)
        DataReady()
    End Sub

    Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles Me.Load
        ...
        ToggleWait(False)
        AddHandler A.GotData, AddressOf _GotData_HandleEvent
        ...
    End Sub
    ...
  End Class

Antworten auf die Frage(1)

Ihre Antwort auf die Frage