2010-02-26 11 views
11

Tengo un formulario de Windows (C# .NET) con una etiqueta de estado que parece que no puedo actualizar en el medio de un proceso en los métodos del controlador de eventos. Mi código es el siguiente ...Por qué no controlará la actualización/actualización a mitad del proceso

void Process_Completed(object sender, EventArgs e) 
    { 

     string t = "Process is finished!"; 
     this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t }); 
    } 

    void Process_Started(object sender, EventArgs e) 
    { 
     string t = "Process has begun"; 
     this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t }); 
    } 

    private delegate void StatusLabelUpdator(string text); 
    private void updateStatusLabel(string text) 
    { 
     StatusLabel1.Text = text; 
     statusStrip1.Invalidate(); 
     statusStrip1.Refresh(); 
     statusStrip1.Update(); 
    } 

Cuando ejecuto el código, una vez iniciado el proceso, se activa el método Process_Started, y un par de segundos más tarde se activa el método Process_Completed. Por algún motivo, no puedo conseguir que la etiqueta de estado muestre siempre "El proceso ha comenzado". ¡Solo muestra "El proceso ha terminado!". Como puede ver, he intentado invalidar, actualizar y actualizar la lista de estado que contiene la etiqueta de estado, pero no ha tenido éxito. No puedo llamar a update/refresh/invalidate en la etiqueta de estado porque esos métodos no están disponibles. ¿Qué estoy haciendo mal?

AÑADIDO INFORMACIÓN:

El "proceso" se inicia mediante un clic de botón en el formulario que llama a un método en una clase separada que tiene este aspecto:

public void DoSomeProcess() 
{ 
    TriggerProcessStarted(); 
    System.Threading.Thread.Sleep(2000); // For testing.. 
    TriggerProcessComplete(); 
} 

y dentro de los métodos TriggerProcessxxxx I desencadenar los eventos utilizando este código ...

var EventListeners = EH.GetInvocationList(); //EH is the appropriate EventHandler 
if (EventListeners != null) 
{ 
    for (int index = 0; index < EventListeners.Count(); index++) 
    { 
     var methodToInvoke = (EventHandler)EventListeners[index]; 
     methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, new object[] { }); 
    } 
} 

Por último, he añadido Application.DoEvents() al método updateStatusLabel pero no sirvió de nada. Todavía estoy obteniendo el mismo resultado. Aquí está mi método de actualización.

private void updateStatusLabel(string text) 
{ 
    StatusLabel1.Text = text; 
    statusStrip1.Refresh(); 
    Application.DoEvents(); 
} 

así que supongo que el "proceso" está teniendo lugar en el hilo de interfaz de usuario, pero eventhandler se invoca en su propio hilo que invoca la actualización de control de vuelta en el hilo de interfaz de usuario. ¿Es esta una forma tonta de hacer las cosas? Nota: La clase que contiene el método DoSomeProcess() está en una .NET ClassLibrary separada a la que me refiero.

Respuesta

16

Si está realizando su proceso en el hilo de la interfaz de usuario, no podrá hacer nada más (como volver a dibujar las etiquetas actualizadas) mientras se está ejecutando el proceso. Entonces, por ejemplo, si el procesamiento está sucediendo porque el usuario hizo clic en un botón y se activa con el botón clic en el controlador (sin colocarlo explícitamente en otro hilo), se está ejecutando en el hilo de la interfaz de usuario. Aunque actualice el texto de la etiqueta, no se dibujará hasta que reciba un mensaje de pintura, en cuyo punto probablemente esté ocupado haciendo su procesamiento.

La respuesta es hacer un procesamiento de larga duración en un separate thread. El truco (en mi humilde opinión) es utilizar Application.DoEvents para permitir que el subproceso de la interfaz de usuario haga algunas cosas de UI durante el procesamiento. Si coloca uno de ellos después de actualizar la etiqueta y antes de comenzar su procesamiento, las probabilidades son bastante altas, la etiqueta se volverá a pintar. Pero luego, durante el procesamiento, no se pueden procesar más eventos de pintura (lo que lleva a ventanas a medio dibujar cuando alguien mueve otra ventana de la aplicación sobre su aplicación y viceversa, etc.). Por lo tanto, lo llamo hack (aunque, eh, um, he sido conocido por hacerlo :-)).

Editar Actualización basada en sus ediciones:

Re

así que supongo que el "proceso" está teniendo lugar en el hilo de interfaz de usuario, pero eventhandler se invoca en su propio hilo ...

Supongo que DoSomeProcess se activa a partir del subproceso de interfaz de usuario (por ejemplo,, en respuesta directa a un clic del botón o similar). Si es así, entonces sí, su procesamiento definitivamente está en el hilo de UI. Como TriggerProcessStarted activa la devolución de llamada de forma asincrónica a través de , no tiene idea de cuándo se ejecutará, pero en cualquier caso su código se inicia inmediatamente en el procesamiento, nunca cede, por lo que nadie más podrá tomar ese hilo. Dado que ese es el hilo de UI, la llamada al delegado se bloqueará en la llamada Invoke, estableciendo el texto de la etiqueta, y luego tendrá que esperar el hilo de UI (que está ocupado procesando). (Y eso suponiendo que esté programado en un hilo diferente; no podría convencerme al 100% de ninguna manera, porque Microsoft tiene dos BeginInvoke s diferentes, que IIRC uno de los diseñadores ha reconocido que era una idea realmente tonta, y ha sido un mientras que luché con esto.)

Si realiza las llamadas TriggerProcessStarted a sus devoluciones de llamada sincrónicas, debería estar bien. Pero idealmente, programe el procesamiento (si no está haciendo UI) en su propio subproceso.

+0

Intenté su "hack" pero no pareció ayudar. Agregué más detalles a la pregunta anterior ahora. – PICyourBrain

+0

DESPUÉS DE EDITAR: Tiene razón. Dejé BeginInvoke solo, pero cambié el evento de clic de botón para iniciar DoSomeWork en su propio hilo. ¡Eso lo solucionó! – PICyourBrain

+0

¡Genial, me alegro de que haya ayudado! –

Cuestiones relacionadas