2010-08-07 10 views
5
private void button1_Click(object sender, RoutedEventArgs e) 
{ 
     int i = 0; 

     while (i < 500) 
     { 
      label1.Content = i.ToString(); 
     // System.Threading.Thread.Sleep(2000); 
      ++i; 
     } 
} 

Estoy tratando de actualizar el contenido de una etiqueta cada vez que se incrementa una variable, pero lo que sucede es que el contenido de label1 solo cambia una vez y solo después de que termine el ciclo while. Pensé que el incremento de la variable del contador era tan rápido que el hilo de la interfaz de usuario no podía alcanzarlo, por lo que quise dejar el hilo inactivo durante 2 segundos, con la esperanza de que la etiqueta 1 cambiara de valor 500 veces. Tampoco funcionó. ¿Por qué?La etiqueta no cambia el valor dentro de un ciclo while

Respuesta

7

Como han dicho otros, el problema es que está bloqueando el hilo de la interfaz de usuario.

En lugar de utilizar las sugerencias con Application.DoEvents, le sugiero que utilice un DispatcherTimer para programar la actualización. Código de ejemplo:

DispatcherTimer timer = new DispatcherTimer(); 
timer.Tick += delegate 
{ 
    label.Content = counter.ToString(); 
    counter++; 
    if (counter == 500) 
    { 
     timer.Stop(); 
    } 
}; 
timer.Interval = TimeSpan.FromMilliseconds(2000); 
timer.Start(); 

(no estoy seguro de si Application.DoEvents funciona incluso en WPF, y está generalmente considerado como una mala práctica de todos modos Si hace trabajo, dudo que se garantiza que funcione en versiones futuras.. la mezcla de los dos interfaces de usuario en la misma aplicación por el simple hecho de que esto suena como una muy mala idea.)

usted podría utilizar unTimer (ya sea System.Threading.Timer o System.Timers.Timer), pero en ambos casos, a continuación, se necesitaría volver a la cadena de distribución de todos modos. DispatcherTimer lo hace más simple: el evento Tick siempre se activa dentro del subproceso Dispatcher de todos modos.

EDIT: Si realmente desea un equivalente de DoEvents, aquí hay código que He verificado funciona, y se basa en la MSDN docs for Dispatcher.PushFrame que explica cómo hacer el equivalente del Application.DoEvents sino de WPF:

public void DoEvents() 
{ 
    DispatcherFrame frame = new DispatcherFrame(); 
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
     new DispatcherOperationCallback(ExitFrames), frame); 
    Dispatcher.PushFrame(frame); 
} 

public object ExitFrames(object f) 
{ 
    ((DispatcherFrame)f).Continue = false; 

    return null; 
} 

private void ButtonClick(object sender, RoutedEventArgs e) 
{ 
    for (int i = 0; i < 500; i++) 
    { 
     label.Content = i.ToString(); 
     DoEvents(); 
    } 
} 

Aún así, no lo recomendaría: WPF (como Windows Forms) está diseñado según un modo de trabajo orientado a eventos.

+0

@drorhan: Claro, debido al temporizador. Eso se ajusta fácilmente. Escogí el momento debido a lo que estaba en la pregunta, sin duda, comentado. Ajustarlo a (digamos) un intervalo de 20 ms lo hará contar bastante rápido. –

+0

@drorhan: (Ahora has editado el comentario) Esto tarda aproximadamente 500 x 2 s (1000 segundos, justo por debajo de los 17 minutos) para terminar. Eso refleja lo que dijo el OP sobre retrasar en 2 segundos. –

+0

@drorhan: ¿Por qué no sería? Son 2000 ms (2 segundos) y se detiene después de 500 iteraciones. No me sorprendería ver que sea un poco más largo en realidad, pero debería ser * más o menos * correcto. –

0

El controlador está acaparando el ciclo de eventos.

Una opción es utilizar el método Application.DoEvents para permitir que se ejecuten las operaciones de pintura. Tenga en cuenta que esto se considera una mala práctica, como lo es el Thread.Sleep que hará que la IU no responda.

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
     int i = 0; 

     while (i < 500) 
     { 
      label1.Content = i.ToString(); 
      System.Threading.Thread.Sleep(2000); 
      Application.DoEvents(); 
      ++i; 
     } 
} 

Otra opción es utilizar Control.BeginInvoke para cada label1.Content = .. operación.

+0

'BeingInvoke' no va a funcionar. El hilo de UI todavía está bloqueado. –

+0

Debería haberlo aclarado: ejecutar el ciclo en otro hilo, realizando actualizaciones de UI con Control.BeginInvoke. – Ani

0

actualización de su método como este:

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
     int i = 0; 

     while (i < 500) 
     { 
      label1.Content = i.ToString(); 
      //Update the UI 
      Application.DoEvents(); 
     // System.Threading.Thread.Sleep(2000); 
      ++i; 
     } 
} 

Este es el método más fácil de hacer esto. Pero no es uno preferido. El preferido utilizará el backgroundworker para tal necesidad.

0

"Si manejamos toda la búsqueda dentro del controlador de evento click del botón, nunca daríamos la posibilidad de que el UI maneje otros eventos. La UI no podría responder a los mensajes de entrada o de proceso. nunca vuelva a pintar y nunca responda a los clics del botón ". - Source.

De todos modos, lo que usted querrá hacer es crear un hilo de trabajo que realice su ciclo for y simplemente actualice la UI usando Dispatcher.BeginInvoke.

-1

Creo que lo que necesita es

Label.Update(); 
Cuestiones relacionadas