¿Por qué se crea una copia de valor de MainForm cuando se llama al método o se invoca un subproceso?
Actualización: creo que tiene algo que ver con la creación de instancias perezosa del identificador de ventana para MainForm, pero no he podido averiguar cómo podría dar como resultado el comportamiento que se observa aquí.
La aplicación solicita datos a través de una interfaz COM de terceros que proporciona una devolución de llamada para procesar los resultados. En la devolución de llamada, la interfaz de usuario debe actualizarse, pero la actualización no funciona como se esperaba. Es como si se hubiera creado una copia de valor de MainForm, cuandoMainForm.DataReady
se llama o se invoca directamente a través de subprocesos, pero la actualización de la interfaz de usuario funciona como se espera cuando se ejecuta desde un controlador de eventos. ¿Puedes explicar porque?
(Nota:AppDomain.CurrentDomain.Id
es siempre1
ya sea examinado en MainForm o en ClassB.)
Codigo inicial - llamar a DataReady desde la instancia de ClassB sin InvokeRequred / Delegate / Invoke logic en MainForm. El cambio de la interfaz de usuario de la aplicación funciona como se esperaba, MainFormSomeListControl.EmptyListMsg = "Not Available"
el cambio no se "pega" (como si se aplicara a una copia separada de MainForm)
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
Se agregó la lógica InvokeRequred a DataReady, aún se llama directamente desde ClassB. InvokeRequired nunca es cierto, el cambio de la interfaz de usuario de la aplicación funciona como se esperaba, MainFormSomeListControl.EmptyListMsg = "Not Available"
el cambio no se "pega" (como si se aplicara a una copia separada de MainForm)
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
InvocadoMainForm.DataReady
directamente desde ClassB Se obtuvo la excepción: "No se puede invocar Invoke o BeginInvoke en un control hasta que se haya creado el identificador de ventana". Hasta que me obligué a crear la ventana de manejar. Entonces es el mismo comportamiento que antes, es decir, InvokeRequired nunca es cierto, el cambio de la interfaz de usuario de la aplicación funciona como se esperaba, MainFormSomeListControl.EmptyListMsg = "Not Available"
el cambio no se "pega" (como si se aplicara a una copia separada de MainForm)
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
Ejecutado desde el controlador de eventos Se definieron eventos personalizados de 'datos obtenidos' en ClassA y ClassB. ClassA escucha ClassB.got_data_event y genera ClassA.got_data_event, MainForm escucha ClassA.got_data_event y lo maneja llamando a DataReady (). Esto funciona: InvokeRequired es verdadero, se invoca Invoke, la interfaz de usuario de la aplicación y los cambios de la interfaz de usuario de MainForm funcionan según lo previsto.
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