2009-06-02 29 views
7

Quería hacer una simple aplicación de cuenta atrás con C# para mostrar como ejemplo.C# Cuenta atrás simple: ¿Qué estoy haciendo mal?

Para la primera y la versión básica utilizo una etiqueta para mostrar el tiempo actual que queda en segundos y un botón para iniciar la cuenta atrás. Click-del botón evento se lleva a cabo de esta manera:

private void ButtonStart_Click(object sender, RoutedEventArgs e) 
    { 
     _time = 60; 
     while (_time > 0) 
     { 
      _time--; 
      this.labelTime.Content = _time + "s"; 
      System.Threading.Thread.Sleep(1000); 
     } 
    } 

Ahora, cuando el usuario hace clic en el botón, el tiempo es en realidad la cuenta atrás (como las congelaciones de aplicación (debido a Sleep())) por la cantidad de tiempo elegido, pero el contexto de la etiqueta no se actualiza.

¿Estoy haciendo algo generalmente incorrecto (cuando se trata de subprocesos) o es solo un problema con la interfaz de usuario?


Gracias por sus respuestas! Ahora uso System.Windows.Threading.DispatcherTimer para hacer lo que me dijiste. Todo funciona bien por lo que esta pregunta se responde oficialmente;)

Para aquellos que estén interesados: Aquí está mi código (las partes esenciales)

public partial class WindowCountdown : Window 
{ 
    private int _time; 
    private DispatcherTimer _countdownTimer; 

    public WindowCountdown() 
    { 
     InitializeComponent(); 
     _countdownTimer = new DispatcherTimer(); 
     _countdownTimer.Interval = new TimeSpan(0,0,1); 
     _countdownTimer.Tick += new EventHandler(CountdownTimerStep); 

    } 

    private void ButtonStart_Click(object sender, RoutedEventArgs e) 
    { 
     _time = 10; 
     _countdownTimer.Start(); 

    } 

    private void CountdownTimerStep(object sender, EventArgs e) 
    { 
     if (_time > 0) 
     { 
      _time--; 
      this.labelTime.Content = _time + "s"; 
     } 
     else 
      _countdownTimer.Stop(); 
    } 
} 

Respuesta

10

Sí, controladores de eventos no deben bloquear - que debe volver inmediatamente. Debe implementar esto mediante un temporizador, un trabajador de fondo o un subproceso (en este orden de preferencia).

9

Lo que está viendo es el efecto de un mensaje de larga duración que bloquea la cola/bomba de mensajes de Windows, que comúnmente asocia con la pantalla de la aplicación en blanco y "no responde". Básicamente, si el hilo está durmiendo, no responde a mensajes como "pintarse a sí mismo". Debe realizar su cambio y ceder el control a la bomba.

Existen varias formas de hacerlo (ripper234 hace un buen trabajo al listarlas). La mala manera general se ven es:

{ // your count/sleep loop 

    // bad code - don't do this: 
    Application.DoEvents(); 
    System.Threading.Thread.Sleep(1000); 
} 

Menciono esto solamente para destacar lo que no hacer; esto causa muchos problemas con la "reentrada" y la administración general del código. Una mejor manera es simplemente usar un Timer, o para un código más complejo, un BackgroundWorker. Algo como:

using System; 
using System.Windows.Forms; 
class MyForm : Form { 
    [STAThread] 
    static void Main() { 
     Application.EnableVisualStyles(); 
     Application.Run(new MyForm()); 
    } 

    Timer timer; 
    MyForm() { 
     timer = new Timer(); 
     count = 10; 
     timer.Interval = 1000; 
     timer.Tick += timer_Tick; 
     timer.Start(); 
    } 
    protected override void Dispose(bool disposing) { 
     if (disposing) { 
      timer.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 
    int count; 
    void timer_Tick(object sender, EventArgs e) { 
     Text = "Wait for " + count + " seconds..."; 
     count--; 
     if (count == 0) 
     { 
      timer.Stop(); 
     } 
    } 
} 
Cuestiones relacionadas