Pregunta

Tengo una aplicación de formulario de Windows que usa una clase compartida para alojar todos los objetos comunes de 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 actualizarlo.

Actualmente estoy haciendo esto a través de Eventos en los objetos, y cuando se crea cada objeto, agrego un EventHandler para asignar el evento nuevamente al formulario. Sin embargo, me encuentro con algunos problemas que sugieren que estas solicitudes no siempre terminan en la copia principal de mi formulario. Por ejemplo, mi formulario tiene un ícono de bandeja de notificación, pero cuando el formulario captura un evento e intenta mostrar una burbuja, no aparece ninguna burbuja. Sin embargo, si modifico ese código para que el ícono sea visible (aunque ya lo es) y luego muestre la burbuja, aparecerá un segundo ícono que mostrará la burbuja correctamente.

¿Alguien se ha encontrado con esto antes? ¿Hay alguna manera de forzar que todos mis eventos sean 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 creo que es un problema común de subprocesos.

MÁS INFORMACIÓN: actualmente estoy usando Me.InvokeRequired en el controlador de eventos en mi formulario, y siempre devuelve FALSE en este caso. Además, el segundo icono de la bandeja creado cuando lo hago visible desde este formulario no tiene un menú contextual, mientras que el " real " icon does - ¿eso da pistas a alguien?

¡Me voy a sacar 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 ahora (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 esta variable se establece y el constructor termina, se dirige directamente al controlador de eventos, y lo trato de esta manera (CAVEAT: Dado que el formulario que estoy buscando es el formulario principal para la aplicación, My.Application .OpenForms (0) obtiene lo que necesito. Si estaba buscando la primera instancia de un formulario que no es de inicio, tendría que recorrerlo hasta que lo encuentre):

    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 lo está, llame al formulario correcto. Luego verifica si el hilo es correcto y llama al hilo de la interfaz de usuario si no lo es. 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!

Nuevamente, gracias por toda la ayuda, ¡esto me volvería loco!

¿Fue útil?

Solución

Creo que también es un problema de subprocesos. ¿Está utilizando Control.Invoke () en su 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 un identificador de ventana para verificar la afinidad del hilo.

Editar después de que OP cambie la pregunta:

Una trampa clásica de VB.NET es hacer referencia a una instancia de Formulario por su nombre de tipo. Como Form1.NotifyIcon1.Something. Eso no funciona como se esperaba cuando usa subprocesos. Creará una instancia nueva de la clase Form1, no usará la instancia existente. Esa instancia no es visible (nunca se llamó a Show ()) y, de lo contrario, está muerta como una uña de la puerta, ya que se ejecuta en un hilo que no bombea un bucle de mensajes. Ver aparecer un segundo ícono es un regalo muerto. Entonces está obteniendo InvokeRequired = False cuando sabes que lo estás usando desde un hilo.

Debe usar una referencia a la instancia de formulario existente. Si eso es difícil de encontrar (por lo general, me pasa '' Me '' como argumento para el constructor de la clase), puede usar Application.OpenForms:

  Dim main As Form1 = CType(Application.OpenForms(0), Form1)
  if (main.InvokeRequired)
    ' etc...

Otros consejos

Use Control.InvokeRequired para determinar si está en el hilo correcto, luego use Control.Invoke si no lo está.

Debe consultar la documentación del método Invocar en el Formulario. Le permitirá hacer que el código que actualiza el formulario se ejecute en el subproceso que posee el formulario (lo que debe hacer, los formularios de Windows no son seguros para subprocesos). Algo como Delegado privado Sub UpdateStatusDelegate (ByVal newStatus as String)

Public sub UpdateStatus (ByVal newStatus as String) If Me.InvokeRequired entonces Dim d As New UpdateStatusDelegate (AddressOf UpdateStatus) Me.Invoke (d, new Object () {newStatus}) Más 'Actualizar el estado del formulario Fin si

Si proporciona algún código de muestra, me complacería proporcionar un ejemplo más personalizado.

Editar después de que OP dijo que están usando InvokeRequired.

Antes de llamar a InvokeRequired, verifique que se haya creado el identificador del formulario, creo que existe una propiedad HandleCreated. InvokeRequired siempre devuelve falso si el control no tiene un identificador actualmente, esto significaría que el código no es seguro para subprocesos aunque haya hecho lo correcto para hacerlo. Actualice su pregunta cuando se entere. Algún código de muestra también sería útil.

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
        }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top