thread - Fuerce la clase VB.NET de subprocesos múltiples para mostrar resultados en una sola forma
ui thread c# (4)
Tengo una aplicación de formulario de Windows que utiliza una clase compartida para alojar todos los objetos comunes para la aplicación. La clase de configuración tiene una colección de objetos que hacen cosas periódicamente, y luego hay algo de interés, necesitan alertar al formulario principal y hacer que se actualice.
Actualmente estoy haciendo esto a través de Eventos en los objetos, y cuando se crea cada objeto, agrego un Manejador de Eventos para mapear el evento de vuelta al formulario. Sin embargo, tengo algunos problemas que sugieren que estas solicitudes no siempre terminan en la copia principal de mi formulario. Por ejemplo, mi formulario tiene un icono de bandeja de notificación, pero cuando el formulario captura y evento e intenta mostrar una burbuja, no aparece ninguna burbuja. Sin embargo, si modifico ese código para hacer que el icono sea visible (aunque ya lo esté), y luego muestre la burbuja, aparece un segundo ícono que muestra la burbuja correctamente.
¿Alguien ha topado con esto antes? ¿Existe alguna manera de forzar todos mis eventos a ser capturados por la única instancia del formulario, o hay una forma completamente diferente de manejar esto? Puedo publicar muestras de código si es necesario, pero estoy pensando que es un problema de subprocesamiento común.
MÁS INFORMACIÓN: Actualmente estoy utilizando Me.InvokeRequired en el controlador de eventos en mi formulario, y siempre devuelve FALSE en este caso. Además, el segundo ícono de bandeja creado cuando lo hago visible desde este formulario no tiene un menú de contexto, mientras que el ícono "real" sí lo hace, ¿eso le da pistas a alguien?
¡Voy a sacarme el pelo! ¡Esto no puede ser tan difícil!
SOLUCIÓN : Gracias a Nobugz por la pista, y me llevó al código que estoy usando (que funciona muy bien, aunque no puedo evitar pensar que hay una mejor manera de hacerlo). Agregué una variable booleana privada al formulario llamado "IsPrimary" y agregué el siguiente código al constructor del formulario:
Public Sub New()
If My.Application.OpenForms(0).Equals(Me) Then
Me.IsFirstForm = True
End If
End Sub
Una vez que se establece esta variable y el constructor finaliza, se dirige directamente al controlador de eventos, y lo trato de esta manera (CAVEAT: dado que el formulario que estoy buscando es el principal para la aplicación, My.Application.OpenForms ( 0) obtiene lo que necesito. Si estuviera buscando la primera instancia de un formulario que no es de inicio, tendría que repetir hasta encontrarlo):
Public Sub EventHandler()
If Not IsFirstForm Then
Dim f As Form1 = My.Application.OpenForms(0)
f.EventHandler()
Me.Close()
ElseIf InvokeRequired Then
Me.Invoke(New HandlerDelegate(AddressOf EventHandler))
Else
'' Do your event handling code ''
End If
End Sub
Primero, verifica si se está ejecutando en el formulario correcto; si no es así, llama al formulario correcto. Luego verifica si el hilo es correcto y llama al hilo de UI si no es así. Luego ejecuta el código del evento. No me gusta que sean potencialmente tres llamadas, pero no puedo pensar en otra forma de hacerlo. Parece funcionar bien, aunque es un poco engorroso. Si alguien tiene una mejor manera de hacerlo, ¡me encantaría escucharlo!
De nuevo, gracias por toda la ayuda, ¡esto me volvería loco!
Debería ver la documentación del método Invoke en el Formulario. Le permitirá hacer que el código que actualiza el formulario se ejecute en el hilo que posee el formulario, (lo cual debe hacer, los formularios de Windows no son seguros para subprocesos). Algo así como Delegado Privado Sub UpdateStatusDelegate (ByVal newStatus como Cadena)
Public sub UpdateStatus (ByVal newStatus como String) If Me.InvokeRequired Then Dim d Como New UpdateStatusDelegate (AddressOf UpdateStatus) Me.Invoke (d, new Object () {newStatus}) Else ''Actualiza el estado del formulario End If If
Si proporciona algún código de muestra, me gustaría proporcionar un ejemplo más personalizado.
Editar después de OP dijo que están utilizando InvokeRequired.
Antes de llamar a InvokeRequired, compruebe que se haya creado el identificador de formulario, creo que hay una propiedad HandleCreated. InvokeRequired siempre devuelve falso si el control no tiene un identificador actual, esto significaría que el código no es seguro para subprocesos aunque hayas hecho lo correcto para que sea así. Actualiza tu pregunta cuando lo descubras. Algunos ejemplos de código también serían útiles.
Use Control.InvokeRequired para determinar si está en el hilo apropiado, luego use Control.Invoke si no lo está.
Creo que es un problema de subprocesamiento también. ¿Estás usando Control.Invoke () en tu controlador de eventos? .NET generalmente detecta violaciones cuando depura la aplicación, pero hay casos en que no puede. NotifyIcon es uno de ellos, no hay ningún identificador de ventana para verificar la afinidad de subprocesos.
Editar después de la pregunta modificada OP:
Una trampa VB.NET clásica es hacer referencia a una instancia de Formulario por su nombre de tipo. Como Form1.NotifyIcon1.Something. Eso no funciona como se esperaba cuando utilizas el enhebrado. Creará una nueva instancia de la clase Form1, no usará la instancia existente. Esa instancia no es visible (nunca se llamó a Show () (y nunca se llamó) y, de lo contrario, está muerta como una entrada porque se está ejecutando en un subproceso que no bombea un bucle de mensaje. Ver aparecer un segundo ícono es un regalo mortal. Entonces, está obteniendo InvokeRequired = False cuando sabe que lo está utilizando desde un hilo.
Debe usar una referencia a la instancia de formulario existente. Si eso es difícil de conseguir (normalmente pasa "Me" como argumento al constructor de la clase), puede usar Application.OpenForms:
Dim main As Form1 = CType(Application.OpenForms(0), Form1)
if (main.InvokeRequired)
'' etc...
en c # se ve así:
private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(StatusHandler, sender, e);
}
else
{
//do work
}
}