¿Cómo puedo hacer que el cursor gire hacia el cursor de espera?

Tengo una aplicación C # que hace que los usuarios accedan a ella, y dado que el algoritmo de hashing es costoso, toma un poco de tiempo hacerlo. ¿Cómo puedo mostrar el Cursor Espera / Ocupado (generalmente el reloj de arena) para que el usuario sepa que el progtwig está haciendo algo?

El proyecto está en C #.

Puede usar Cursor.Current .

 // Set cursor as hourglass Cursor.Current = Cursors.WaitCursor; // Execute your time-intensive hashing code here... // Set cursor as default arrow Cursor.Current = Cursors.Default; 

Sin embargo, si la operación de hash es realmente larga (MSDN lo define como más de 2-7 segundos), probablemente deba usar un indicador de realimentación visual que no sea el cursor para notificar al usuario del progreso. Para obtener un conjunto más detallado de directrices, consulte este artículo .

Editar:
Como @Am señaló, es posible que deba llamar a Application.DoEvents(); después de Cursor.Current = Cursors.WaitCursor; para asegurarse de que el reloj de arena realmente se muestre.

Actualmente,

 Cursor.Current = Cursors.WaitCursor; 

Establece temporalmente el cursor de Espera, pero no garantiza que el cursor de Espera se muestre hasta el final de su operación. Otros progtwigs o controles dentro de su progtwig pueden restablecer fácilmente el cursor de vuelta a la flecha predeterminada, ya que de hecho ocurre cuando mueve el mouse mientras la operación todavía está ejecutándose.

Una forma mucho mejor de mostrar el cursor Wait es establecer la propiedad UseWaitCursor en un formato verdadero:

 form.UseWaitCursor = true; 

Esto mostrará el cursor de espera para todos los controles en el formulario hasta que establezca esta propiedad en falso. Si desea que el cursor de espera se muestre en el nivel de aplicación, debe usar:

 Application.UseWaitCursor = true; 

Sobre la base de lo anterior, mi enfoque preferido (ya que esta es una acción que se realiza con frecuencia) es ajustar el código del cursor de espera en una clase auxiliar IDisposable para que se pueda usar con () (una línea de código), tomar parámetros opcionales, ejecutar el código dentro, luego limpiar (restaurar el cursor) después.

 public class CursorWait : IDisposable { public CursorWait(bool appStarting = false, bool applicationCursor = false) { // Wait Cursor.Current = appStarting ? Cursors.AppStarting : Cursors.WaitCursor; if (applicationCursor) Application.UseWaitCursor = true; } public void Dispose() { // Reset Cursor.Current = Cursors.Default; Application.UseWaitCursor = false; } } 

Uso:

 using (new CursorWait()) { // Perform some code that shows cursor } 

Es más fácil usar UseWaitCursor en el nivel de Forma o Ventana. Un caso de uso típico puede verse a continuación:

  private void button1_Click(object sender, EventArgs e) { try { this.Enabled = false;//optional, better target a panel or specific controls this.UseWaitCursor = true;//from the Form/Window instance Application.DoEvents();//messages pumped to update controls //execute a lengthy blocking operation here, //bla bla .... } finally { this.Enabled = true;//optional this.UseWaitCursor = false; } } 

Para una mejor experiencia de UI, debes usar Asynchrony desde un hilo diferente.

Mi enfoque sería hacer todos los cálculos en un trabajador de segundo plano.

A continuación, cambie el cursor de esta manera:

 this.Cursor = Cursors.Wait; 

Y en el evento de finalización del hilo restaura el cursor:

 this.Cursor = Cursors.Default; 

Tenga en cuenta que esto también se puede hacer para controles específicos, por lo que el cursor será el reloj de arena solo cuando el mouse esté sobre ellos.

OK, así que creé un método asíncrono estático. Eso deshabilitó el control que inicia la acción y cambia el cursor de la aplicación. Ejecuta la acción como una tarea y espera para terminar. El control vuelve a la persona que llama mientras espera. De modo que la aplicación sigue siendo receptiva, incluso mientras el ícono de ocupado gira.

 async public static void LengthyOperation(Control control, Action action) { try { control.Enabled = false; Application.UseWaitCursor = true; Task doWork = new Task(() => action(), TaskCreationOptions.LongRunning); Log.Info("Task Start"); doWork.Start(); Log.Info("Before Await"); await doWork; Log.Info("After await"); } finally { Log.Info("Finally"); Application.UseWaitCursor = false; control.Enabled = true; } 

Aquí está el código de la forma principal

  private void btnSleep_Click(object sender, EventArgs e) { var control = sender as Control; if (control != null) { Log.Info("Launching lengthy operation..."); CursorWait.LengthyOperation(control, () => DummyAction()); Log.Info("...Lengthy operation launched."); } } private void DummyAction() { try { var _log = NLog.LogManager.GetLogger("TmpLogger"); _log.Info("Action - Sleep"); TimeSpan sleep = new TimeSpan(0, 0, 16); Thread.Sleep(sleep); _log.Info("Action - Wakeup"); } finally { } } 

Tuve que usar un registrador separado para la acción ficticia (estoy usando Nlog) y mi registrador principal está escribiendo en la interfaz de usuario (un cuadro de texto enriquecido). No pude ver el cursor ocupado solo cuando estaba sobre un contenedor en particular en el formulario (pero no lo intenté muy duro). Todos los controles tienen una propiedad UseWaitCursor, pero no parece tener ningún efecto en los controles Lo intenté (¿tal vez porque no estaban en la cima?)

Aquí está el registro principal, que muestra las cosas que suceden en el orden que esperamos:

 16:51:33.1064 Launching lengthy operation... 16:51:33.1215 Task Start 16:51:33.1215 Before Await 16:51:33.1215 ...Lengthy operation launched. 16:51:49.1276 After await 16:51:49.1537 Finally 

Okey, el punto de vista de otras personas es muy claro, pero me gustaría agregar algunos, como se muestra a continuación:

 Cursor tempCursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; //do Time-consuming Operations Cursor.Current = tempCursor; 

Con la clase a continuación puede hacer la sugerencia de Donut “excepción segura”.

 using (new CursorHandler()) { // Execute your time-intensive hashing code here... } 

la clase CursorHandler

 public class CursorHandler : IDisposable { public CursorHandler(Cursor cursor = null) { _saved = Cursor.Current; Cursor.Current = cursor ?? Cursors.WaitCursor; } public void Dispose() { if (_saved != null) { Cursor.Current = _saved; _saved = null; } } private Cursor _saved; } 

Use esto con WPF:

 Cursor = Cursors.Wait; // Your Heavy work here Cursor = Cursors.Arrow;