it-swarm.dev

Uso de Application.DoEvents ()

¿Se puede utilizar 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 DoEvents de VB6?

249
Craig Johnston

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 con 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.

13
Craig Johnston

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 los mensajes de Windows y recibe todas las solicitudes de Paint entregadas. Sin embargo, el problema es que no es selectivo. No solo envía mensajes de Paint, 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 ejecutando 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. Es deshabilita todas las ventanas en la aplicación , aparte del 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 para decirlo de otra manera, el usuario no puede 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 Habilitado 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 el problema causado por DoEvents y subprocesos, pero en gran parte por el diseño de la API de WinRT que requiere mantener su interfaz de usuario actualizada mientras se realiza una operación asíncrona. Como leer de un archivo.

446
Hans Passant

Puede ser, pero es un truco.

Ver¿DoEvents es malo?.

Directamente desde la página de MSDN that thedev referenciada:

Llamar a este método hace que el hilo actual se suspenda 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 al intentar usar DoEvents en lugar de girar un hilo nuevo 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.

27
RQDQ

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 en espera en la cola en el subproceso de la IU cuando se realiza una tarea de larga ejecución en el subproceso de la IU. 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 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 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. Mira aquí para más detalles.

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

23
Bill W

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 algún trabajo en un hilo separado en su lugar?

11

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, utilizan el hack, porque no conocen ninguna otra solución. Aquí hay algunos enfoques tomados de una publicación de blog por Tom Miller :

  • Configure su formulario para que todos los dibujos aparezcan en WmPaint y haga su renderización allí. Antes de que finalice el método OnPaint, asegúrese 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 en realidad hace algo similar, pero puede hacer esto 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 ciclo de mensajes. -Decidir que el método DoEvents funciona bien para usted y seguirlo.
4
Matthias

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 aquí ) para asegurar que una pantalla de presentación de tipo de barra de progreso ( Cómo mostrar una superposición de "carga" ... ) actualizada 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 disparaban en los formularios detrás de la pantalla de bienvenida, incluso si lo hacía en la parte superior.

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

4
TamW

Consulte la documentación de MSDN para el método Application.DoEvents .

3
thedev

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. En este caso, el subproceso de la interfaz de usuario estaba ocupado con el envío de un correo electrónico en un servidor SMTP que no era compatible con las 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.

2
Guest123

DoEvents le permite al usuario hacer clic o escribir y desencadenar otros eventos, y los subprocesos en segundo plano son un mejor enfoque.

Sin embargo, todavía hay casos en los que puede tener problemas que requieren la descarga de mensajes de eventos. Me encontré con un problema donde el control RichTextBox ignoraba el método ScrollToCaret () cuando el control tenía mensajes en cola para procesar.

El siguiente código bloquea todas las entradas del usuario al ejecutar DoEvents:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Integrative.Desktop.Common
{
    static class NativeMethods
    {
        #region Block input

        [DllImport("user32.dll", EntryPoint = "BlockInput")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);

        public static void HoldUser()
        {
            BlockInput(true);
        }

        public static void ReleaseUser()
        {
            BlockInput(false);
        }

        public static void DoEventsBlockingInput()
        {
            HoldUser();
            Application.DoEvents();
            ReleaseUser();
        }

        #endregion
    }
}
1