c# - alternative - Uso de Application.DoEvents()
my application doevents vb net (9)
¿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 DoEvents
hace DoEvents
de VB6?
Application.DoEvents puede crear problemas, si se pone en la cola de mensajes algo distinto al procesamiento de gráficos.
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 lleva un tiempo.
En una aplicación reciente que hice, usé 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 subproceso de la interfaz de usuario, en este caso, estaba ocupado con el envío de un correo electrónico en un servidor SMTP que no admitía llamadas SendAsync (). Probablemente podría haber creado un subproceso diferente con los métodos Begin () y End () y haber llamado a Send (), 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.
Desde mi experiencia, aconsejaría mucho cuidado 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ás tratando es un formulario pequeño con una barra de progreso, entonces podría estar bien.
La conclusión es: si va a usar DoEvents, entonces necesita probarlo a fondo antes de implementar su aplicación.
Echa un vistazo a la documentación de MSDN para el método Application.DoEvents
.
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 conocen el mal de ese método. Sin embargo, usan el hack, porque no conocen ninguna otra solución. Aquí hay algunos enfoques tomados de una publicación de blog de Tom Miller :
- Configure su formulario para que todos los dibujos aparezcan en WmPaint y haga su renderización allí. Antes del final del método OnPaint, asegúrate de hacer un this.Invalidate (); Esto hará que el método OnPaint se vuelva a disparar inmediatamente.
- P / Invocar en la API de Win32 y llamar a PeekMessage / TranslateMessage / DispatchMessage. (Doevents realmente hace algo similar, pero puede hacerlo sin las asignaciones adicionales).
- Escriba su propia clase de formularios que es un pequeño envoltorio alrededor de CreateWindowEx, y dése un control completo sobre el bucle de mensajes. -Decidir que el método DoEvents funciona bien para usted y seguirlo.
Hmya, la mística perdurable de DoEvents (). Ha habido una enorme cantidad de reacciones violentas en su contra, pero nadie explica realmente por qué es "malo". El mismo tipo de sabiduría como "no mutar una estructura". Erm, ¿por qué el tiempo de ejecución y el lenguaje admiten la mutación de una estructura si eso es tan malo? La misma razón: te disparas en el pie si no lo haces bien. Fácilmente. Y hacerlo bien requiere saber exactamente lo que hace, lo que en el caso de DoEvents () definitivamente no es fácil de asimilar.
De inmediato: casi cualquier programa de Windows Forms en realidad 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 que se congele el resto de las ventanas de 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 hace eso; despacha mensajes de Windows y recibe todas las solicitudes de pintura entregadas. El problema, sin embargo, 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 por delante del monitor. El usuario podría, por ejemplo, cerrar la ventana principal mientras se ejecuta el ciclo 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 mal.
Hay más: el usuario puede hacer clic en el mismo elemento de menú o botón que hace que se inicie el mismo bucle. Ahora tiene dos bucles anidados que ejecutan DoEvents (), el bucle anterior se suspende y el nuevo bucle comienza de cero. Eso podría funcionar, pero las probabilidades son escasas. Especialmente cuando el bucle anidado termina y el suspendido se reanuda, intentando finalizar un trabajo que ya se completó. Si eso no bombardea con una excepción, entonces seguramente los datos están revueltos al infierno.
Volver a ShowDialog (). Ejecuta DoEvents (), pero tenga en cuenta que hace otra cosa. Deshabilita todas las ventanas de la aplicación , excepto el diálogo. Ahora que el problema de los 3 pies está resuelto, el usuario no puede hacer nada para estropear la lógica. Se resuelven los modos de falla tanto cerrar la ventana como iniciar el trabajo nuevamente. O, dicho de otra manera, el usuario no tiene forma de hacer que su programa ejecute el código en un orden diferente. Se ejecutará de manera predecible, tal como lo hizo cuando probó su 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 falso es una forma rápida y eficiente de evitar problemas. Por supuesto, a ningún programador le gusta hacer esto. Y no lo hace Es por eso que no debes usar DoEvents (). Deberías usar hilos. A pesar de que te entregan un completo arsenal de formas para disparar tu pie de maneras coloridas e inescrutables. Pero con la ventaja de que solo disparas tu propio pie; no permitirá (típicamente) al usuario disparar el suyo.
Las próximas versiones de C # y VB.NET proporcionarán un arma diferente con las nuevas palabras clave de espera y asíncrona. Inspirado en pequeña parte por los problemas causados por DoEvents y subprocesos, pero en gran parte por el diseño de la API de WinRT que requiere que mantenga su interfaz de usuario actualizada mientras se realiza una operación asíncrona. Como leer de un archivo.
Puede ser, pero es un hack.
Ver es DoEvents Evil? .
Directamente desde la página de MSDN a la que se thedev referencia:
Al llamar a este método, el hilo actual se suspende mientras se procesan todos los mensajes de la ventana en espera. Si un mensaje hace que se active un evento, es posible que se ejecuten otras áreas del código de su aplicación. Esto puede hacer que su aplicación muestre comportamientos inesperados que son difíciles de depurar. Si realiza operaciones o cálculos que llevan mucho tiempo, a menudo es preferible realizar esas operaciones en un nuevo hilo. Para obtener más información acerca de la programación asíncrona, consulte Descripción general de la programación asíncrona.
Así que Microsoft advierte contra su uso.
Además, lo considero un pirateo porque su comportamiento es impredecible y propenso a los efectos secundarios (esto se debe a la experiencia que intenta usar DoEvents en lugar de girar un nuevo hilo o usar un trabajador de fondo).
Aquí no hay machismo, si funcionara como una solución robusta, estaría por encima de todo. Sin embargo, intentar usar DoEvents en .NET no me ha causado más que dolor.
Sí, hay un método DoEvents estático en la clase de aplicación en el espacio de nombres System.Windows.Forms. System.Windows.Forms.Application.DoEvents () se puede usar para procesar los mensajes que esperan en la cola en el subproceso de la interfaz de usuario cuando se realiza una tarea de larga ejecución en el subproceso de la interfaz de usuario. Esto tiene la ventaja de hacer que la interfaz de usuario parezca más receptiva y no "bloqueada" mientras se está ejecutando una tarea larga. Sin embargo, esto casi siempre NO es la mejor manera de hacer las cosas. Según Microsoft llamando a DoEvents "... hace que el subproceso 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 tienes una tarea extensa, es mucho mejor hacerlo en un hilo separado. La ejecución de tareas largas en un subproceso separado permite que se procesen sin interferir con la IU que continúa ejecutándose sin problemas. Busque here para más detalles.
Here hay un ejemplo de cómo usar DoEvents; tenga en cuenta que Microsoft también proporciona una precaución contra su uso.
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 trabajo en un hilo separado en su lugar?
Vi el comentario de jheriko arriba e inicialmente acepté que no podrí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 un fragmento de código asíncrono de larga duración en otro hilo. Pero a partir de la respuesta de Matthias, un simple Refresh de un pequeño panel en mi interfaz de usuario puede reemplazar los DoEvents (y evitar un efecto secundario desagradable).
Más detalles sobre mi caso ...
Estaba haciendo lo siguiente (como se sugiere here ) para asegurar que una pantalla de presentación de tipo de barra de progreso ( Cómo mostrar una superposición de "carga" ... ) se actualizó 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 mouse a veces se disparaban en los formularios detrás de mi pantalla de bienvenida, incluso si lo hacía TopMost.
La respuesta correcta : reemplace la línea DoEvents con una simple llamada de actualización a un pequeño panel en el centro de mi pantalla de FormSplash.Panel1.Refresh()
, FormSplash.Panel1.Refresh()
. La interfaz de usuario se actualiza bien y la rareza de DoEvents que otros han advertido se ha ido.