Uso de la Aplicación.DoEvents()


¿Se puede usar Application.DoEvents() en C#?

¿Es esta función una forma de permitir que la GUI se ponga al día con el resto de la aplicación, de la misma manera que lo hace VB6 DoEvents?

Author: Daniel B, 2011-03-03

9 answers

Hmya, la mística duradera de DoEvents(). Ha habido una enorme cantidad de reacciones en su contra, pero nadie explica realmente por qué es "malo". El mismo tipo de sabiduría que "no mutar una estructura". ¿Por qué el tiempo de ejecución y el lenguaje soportan mutar una estructura si eso es tan malo? La misma razón: te disparas en el pie si no lo haces bien. Facilmente. Y hacerlo bien requiere saber exactamente lo que hace, lo que en el caso de DoEvents() definitivamente no es fácil de grok.

Desde el principio: casi cualquier programa de Windows Forms realmente contiene una llamada a DoEvents(). Está hábilmente disfrazado, sin embargo con un nombre diferente: ShowDialog(). Es DoEvents () que permite que un diálogo sea modal sin congelar el resto de ventanas en la aplicación.

La mayoría de los programadores quieren usar DoEvents para evitar que su interfaz de usuario se congele cuando escriben su propio bucle modal. Ciertamente lo hace; envía mensajes de Windows y obtiene cualquier solicitud de pintura entregada. Sin embargo, el problema es que no es selectivo. No solo envía mensajes de pintura, sino que también entrega todo lo demás.

Y hay un conjunto de notificaciones que causan problemas. Vienen de unos 3 pies delante del monitor. El usuario podría, por ejemplo, cerrar la ventana principal mientras se ejecuta el bucle que llama a DoEvents (). Eso funciona, la interfaz de usuario se ha ido. Pero su código no se detuvo, todavía está ejecutando el bucle. Eso es malo. Muy, muy malo.

Hay más: El usuario puede hacer clic en el mismo elemento de menú o botón que hace que el mismo bucle comience. Ahora tiene dos bucles anidados ejecutando DoEvents (), el bucle anterior se suspende y el nuevo bucle comienza desde cero. Eso podría funcionar, pero las probabilidades son escasas. Especialmente cuando el bucle anidado termina y el suspendido se reanuda, tratando de terminar un trabajo que ya se completó. Si eso no bombea con una excepción, entonces seguramente los datos se codifican todos para infierno.

Volver a ShowDialog(). Ejecuta DoEvents (), pero ten en cuenta que hace otra cosa. desactiva todas las ventanas de la aplicación, excepto el diálogo. Ahora que el problema de 3 pies está resuelto, el usuario no puede hacer nada para estropear la lógica. Se resuelven los modos de fallo cerrar la ventana y volver a iniciar el trabajo. O para decirlo de otra manera, no hay manera para que el usuario haga que su programa ejecute el código en un orden diferente. Se ejecutará de manera predecible, al igual que lo hizo cuando probaste tu código. Hace que los diálogos sean extremadamente molestos; ¿quién no odia tener un diálogo activo y no poder copiar y pegar algo desde otra ventana? Pero ese es el precio.

Que es lo que se necesita para usar DoEvents de forma segura en su código. Establecer la propiedad Habilitada de todos sus formularios en false es una forma rápida y eficiente de evitar problemas. Por supuesto, a ningún programador le gusta hacer esto. Y no lo hace. Por eso no deberías usar DoEvents(). Usted debería usar hilos. A pesar de que te dan un arsenal completo de formas de disparar tu pie de maneras coloridas e inescrutables. Pero con la ventaja de que solo disparas tu propio pie; no permitirá (típicamente) que el usuario dispare el suyo.

Las siguientes versiones de C# y VB.NET proporcionará un arma diferente con las nuevas palabras clave await y async. Inspirado en pequeña parte por los problemas causados por DoEvents y threads, pero en gran parte por el diseño de la API de WinRT que requiere que mantenga su La interfaz de usuario se actualiza mientras se realiza una operación asíncrona. Como leer de un archivo.

 427
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-23 10:20:55

Puede ser, pero es un truco.

Véase ¿DoEvents es Malo?.

Directamente desde la página de MSDN que thedev hace referencia:

Llamar a este método causa la corriente hilo para ser suspendido mientras todos los mensajes de la ventana en espera se procesan. Si un mensaje hace que un evento sea disparada, luego otras áreas de su el código de la aplicación puede ejecutarse. Esto puede haga que su solicitud exhiba comportamientos inesperados que son difícil de depurar. Si realizas operaciones o cálculos que toman una mucho tiempo, a menudo es preferible realizar esas operaciones en un nuevo hilo. Para más información sobre programación asíncrona, véase Visión General de la Programación Asíncrona.

Así que Microsoft advierte contra su uso.

Además, lo considero un truco porque su comportamiento es impredecible y propenso a los efectos secundarios (esto proviene de la experiencia al tratar de usar DoEvents en lugar de girar un nuevo hilo o usando background worker).

Aquí no hay machismo - si funcionara como una solución robusta estaría por todas partes. Sin embargo, tratar de usar DoEvents en.NET no me ha causado más que dolor.

 27
Author: RQDQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:06

Sí, hay un método DoEvents estático en la clase Application en el Sistema.Windows.Espacio de nombres de formularios. Sistema.Windows.Forma.Aplicación.DoEvents () se puede usar para procesar los mensajes en espera en la cola en el subproceso de IU cuando se realiza una tarea de larga duración en el subproceso de IU. Esto tiene la ventaja de hacer que la interfaz de usuario parezca más receptiva y no "bloqueada" mientras se ejecuta una tarea larga. Sin embargo, esta casi siempre no es la mejor manera de hacer las cosas. Según Microsoft llamando a DoEvents "...hace que el hilo actual se suspenda mientras se procesan todos los mensajes de la ventana de espera."Si se desencadena un evento, existe la posibilidad de errores inesperados e intermitentes que son difíciles de rastrear. Si tiene una tarea extensa, es mucho mejor hacerlo en un hilo separado. Ejecutar tareas largas en un subproceso separado permite procesarlas sin interferir con la interfaz de usuario que continúa ejecutándose sin problemas. Mira aquí para más detalles.

Aquí es un ejemplo de cómo usar DoEvents; tenga en cuenta que Microsoft también proporciona una advertencia contra su uso.

 23
Author: Bill W,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-26 19:23:02

Desde mi experiencia aconsejaría mucha precaución con el uso de DoEvents en .NET. Experimenté algunos resultados muy extraños al usar DoEvents en un TabControl que contiene DataGridViews. Por otro lado, si todo lo que está tratando es una pequeña forma con una barra de progreso, entonces podría estar bien.

La conclusión es: si va a usar DoEvents, debe probarlo a fondo antes de implementar su aplicación.

 14
Author: Craig Johnston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-20 14:54:32

Sí.

Sin embargo, si necesita usar Application.DoEvents, esto es principalmente una indicación de un mal diseño de la aplicación. Tal vez le gustaría hacer un poco de trabajo en un hilo separado en su lugar?

 10
Author: Frederik Gheysels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-24 13:59:08

He visto muchas aplicaciones comerciales, usando el "DoEvents-Hack". Especialmente cuando el renderizado entra en juego, a menudo veo esto:

while(running)
{
    Render();
    Application.DoEvents();
}

Todos saben acerca de la maldad de ese método. Sin embargo, usan el hack, porque no conocen ninguna otra solución. Aquí hay algunos enfoques tomados de una entrada de blog de Tom Miller :

  • Establezca su formulario para que todos los dibujos ocurran en WmPaint, y haga su renderizado allí. Antes del final del OnPaint método, asegúrese de hacer un esto.Invalidate (); Esto hará que el método OnPaint se dispare de nuevo inmediatamente.
  • P/Invoke en la API Win32 y llame a PeekMessage/TranslateMessage/DispatchMessage. (Doevents en realidad hace algo similar, pero puede hacer esto sin las asignaciones adicionales).
  • Escribe tu propia clase forms que sea una pequeña envoltura alrededor de CreateWindowEx, y date un control completo sobre el bucle de mensajes. - Decidir que el método DoEvents funciona bien para ti y quédate con él.
 4
Author: Matthias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-19 21:59:06

Vi el comentario de jheriko arriba y estaba inicialmente de acuerdo en que no podía encontrar una manera de evitar el uso de DoEvents si terminas girando tu hilo principal de la interfaz de usuario esperando que se complete una pieza de código asincrónica de larga ejecución en otro hilo. Pero a partir de la respuesta de Matthias, una simple actualización de un pequeño panel en mi interfaz de usuario puede reemplazar los DoEvents (y evitar un desagradable efecto secundario).

Más detalles sobre mi caso ...

Estaba haciendo lo siguiente (como se sugiere aquí) para asegurar que un barra de progreso tipo splash screen ( Cómo mostrar una superposición de "carga"...) actualizado durante un comando SQL de larga ejecución:

IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted)  //UI thread needs to Wait for Async SQL command to return
{
      System.Threading.Thread.Sleep(10); 
      Application.DoEvents();  //to make the UI responsive
}

Lo malo: Para mí llamar a DoEvents significaba que los clics del ratón a veces se disparaban en los formularios detrás de mi pantalla de bienvenida, incluso si lo hacía más alto.

El bueno / respuesta: Reemplace la línea DoEvents con una simple llamada de actualización a un pequeño panel en el centro de mi pantalla de bienvenida, FormSplash.Panel1.Refresh(). La interfaz de usuario se actualiza muy bien y la rareza DoEvents otros han advertido de que se había ido.

 3
Author: TamW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:34

Solicitud.DoEvents puede crear problemas, si algo que no sea el procesamiento de gráficos se pone en la cola de mensajes.

Puede ser útil para actualizar las barras de progreso y notificar al usuario del progreso en algo como la construcción y carga de MainForm, si eso toma un tiempo.

En una aplicación reciente que he hecho, utilicé DoEvents para actualizar algunas etiquetas en una pantalla de carga cada vez que se ejecuta un bloque de código en el constructor de mi MainForm. El hilo de interfaz de usuario fue, en este caso, ocupado con el envío de un correo electrónico en un servidor SMTP que no soporta llamadas SendAsync (). Probablemente podría haber creado un subproceso diferente con los métodos Begin() y End() y llamar a Send() desde sus, pero ese método es propenso a errores y preferiría que la Forma Principal de mi aplicación no arrojara excepciones durante la construcción.

 2
Author: Guest123,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-20 16:43:52

Echa un vistazo a la documentación de MSDN para Application.DoEvents método.

 2
Author: thedev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-24 13:58:14