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