2009-03-15 37 views
10

He heredado una aplicación de Winforms que hace muchas llamadas de larga ejecución en el servidor de aplicaciones desde el hilo de UI, por lo que la UI no responde, inutilizable, imposible de cerrar desde hace bastante tiempo . (Lo cual me hace muy vaya AAAAAAAAARGH!)Cómo bloquear la interfaz de usuario de Winforms mientras se ejecuta el hilo de fondo

planeo mover el servidor llama a un subproceso de fondo y tener la interfaz de usuario para minusválidos - pero se puede cerrar móvil & - mientras que el subproceso de fondo hace su trabajo.

Entonces, ¿cuál sería la mejor manera de inhibir la entrada del usuario a mi aplicación? Estoy pensando en el "diálogo modal de progreso", pero preferiría una solución que no me obligue a mostrar imágenes en la cara del usuario (algunas operaciones del servidor se ejecutan en menos de 500ms, por lo que un cuadro de diálogo es no óptimo ...)

¿Hay alguna forma en Winforms para evitar que el usuario inicie acciones o cambie datos en la aplicación mientras permite seleccionar algunas cosas (cambiar el tamaño, mostrar, ocultar y familia y el usuario cierra la ventana)? Preferiría una forma que no me haga acceder a todos los elementos de la interfaz de usuario en mis formularios y configurarlo como deshabilitado ... hay bastantes de ellos y esa aplicación realmente tiene un "pirateado en el diseñador de la interfaz de usuario hasta que muestra cosas llamativas "estilo de código fuente. No hay manera de refactorización CADA cosa maloliente hasta la fecha de lanzamiento ...

Ah, por cierto, esta aplicación vive en .NET Framework 2

+0

¿Por qué no se desactiva el botón que inició la llamada de larga ejecución? –

+0

La interfaz de usuario es un formulario de entrada de datos, la llamada de larga ejecución podría ser un "guardado" que involucra muchos objetos de dominio. Quiero evitar que el usuario cambie datos en el formulario mientras se ejecuta el guardado, por ejemplo. Por cierto ESTO (http://stuffthathappens.com/blog/2008/03/05/simplicity) es TAN verdadero ... – froh42

+0

¿Cuándo quieres habilitar de nuevo el formulario? Si intenta hacer eso en el evento "_RunWorkerCompleted" obtendrá una excepción porque no tiene permitido (http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx - primera nota). Solo puede hacerlo manualmente para cada elemento en el formulario o para el primer contenedor. – Edd

Respuesta

6

La única forma que conozco es conseguir algunos/todos los controles como discapacitados . Sin embargo, dependiendo de cómo se distribuyan los controles, no es necesario configurar cada control como desactivado: cuando un control de contenedor está desactivado, todos sus elementos secundarios también se deshabilitan. Por ejemplo, si tiene un GroupBox con algunos controles, si configura el GroupBox en deshabilitado, entonces todos los controles dentro del GroupBox también estarán deshabilitados.

+0

no es posible: el funcionamiento entre subprocesos no es válido: controle 'groupBox2' al que se accede desde un subproceso que no sea el subproceso en el que se creó. :( – Edd

+1

@Edd Necesitará deshabilitar la interfaz de usuario del hilo de la interfaz de usuario, no la cadena de fondo. Entonces funcionará. – Andy

+0

tiene razón, pero ¿cómo lo habilita de nuevo cuando el proceso se completa en el trabajador de segundo plano? ¿señaliza a la forma de UI para habilitar? porque del evento completo, no puede. – Edd

5

Elija un control de contenedor de nivel superior (podría ser el formulario en sí) y establezca su propiedad Enabled en false. Todos los controles colocados en ese control se desactivarán, lo que impedirá que reciban información.

0

Desafortunadamente, con WinForms, es probable que deba deshabilitar/habilitar los botones en un solo método compartido. Esta es una de las ventajas que WPF aporta a los formularios de Windows: es mucho más fácil manejar estas situaciones.

En cuanto a no bloquear su IU, tendrá que mover sus llamadas a un modelo de programación asíncrona. En tu caso, recomendaría echar un vistazo al ThreadPool.

Querrá hacer algo así como hacer que el botón deshabilite sus controles, llame su operación en el grupo de subprocesos, luego vuelva a habilitarlos al finalizar.

1

En el nivel API de Win32, hay una función EnableWindow con la que puede deshabilitar cualquier manecilla de ventana (necesita obtener el identificador subyacente de su ventana principal, por supuesto, y de cualquier otra ventana de nivel superior). Sin embargo, no lo he probado en WinForms. (Estoy bastante seguro de que hay otras respuestas en el nivel User32, pero no recuerdo las llamadas a la API en este momento).

Nota: esto no atenúa los controles, lo que disminuye su utilidad desde un punto de usabilidad de vista (el usuario solo ve una 'aplicación congelada'). Se pone marginalmente mejor si al menos se establece un cursor de reloj de arena.

Sin embargo, esta es una forma rápida y sucia de tratar con el procesamiento asincrónico. ¿Realmente necesita bloquear al usuario de toda su aplicación?¿Tal vez sean solo algunas las funciones que deberían prohibirse mientras se realiza una solicitud?

3

El problema con simplemente permitiendo botones/desactivación es que los eventos de interfaz de usuario todavía se agregarán a la MessageQueue mientras se está ejecutando la tarea ....

Digamos que tiene un botón de búsqueda y un botón de reinicio. Cuando hace clic en Buscar, desactiva ambos botones. La búsqueda se ejecuta por un tiempo. Si el usuario hace clic en Restablecer, incluso mientras está desactivado, la búsqueda se restablecerá inmediatamente después de completar.

La forma más rápida y sucia de evitar esto es añadir una llamada

Application.DoEvents(); 

inmediatamente antes de volver a habilitar los botones. DoEvents() procesará todos los eventos de clic en los controles, que se ignorarán para los controles que aún están deshabilitados.

+0

Oh, eso es * terrible *. Lo estoy usando. Saludos. – Frosty840

0

Estoy respondiendo un hilo viejo aquí, pero vi esta pregunta y recordé una aplicación WinForm mía donde tuve el mismo problema de desactivar un formulario al mismo tiempo que permitía que se cambiara el tamaño y se moviera, etc. Cargué mi código y encontré que la solución era:

  1. Coloque un control Panel en el formulario y otorgue a la propiedad Dock un valor de DockStyle.Fill.
  2. En lugar de colocar todos los controles de nivel superior constituyentes del formulario directamente en el formulario, colóquelos en el control del Panel.
  3. Ahora, para el bit mágico: cuando quiera deshabilitar los controles del formulario, simplemente establezca la propiedad Habilitada del Panel en falso. Al hacer esto, todos los controles contenidos por el control del Panel también se deshabilitarán, pero el formulario en sí todavía está habilitado, y por lo tanto se puede cambiar de tamaño, etc. Para restablecer el estado de todos los controles, simplemente establezca la propiedad Enabled en true.

Estrictamente hablando, no tiene que usar un control de Panel, cualquier Control de Contenedor debe exhibir el mismo comportamiento. ¡Incluso un control PictureBox hará el truco!

Cuestiones relacionadas