c# - quitar - ¿Cómo puedo hacer que el cursor gire hacia el cursor de espera?
cursor animado (9)
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 programas o controles dentro de su programa pueden restablecer fácilmente el cursor a la flecha predeterminada, ya que, de hecho, sucede cuando mueve el mouse mientras la operación aún se está ejecutando.
Una forma mucho mejor de mostrar el cursor de Espera es establecer la propiedad UseWaitCursor en un formulario en 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 utilizar:
Application.UseWaitCursor = true;
Tengo una aplicación de C # que tiene usuarios que inician sesión en ella, y como el algoritmo de hash es caro, toma un poco de tiempo hacerlo. ¿Cómo puedo mostrar el cursor de espera / ocupado (generalmente el reloj de arena) al usuario para informarle que el programa está haciendo algo?
El proyecto está en C #.
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;
}
Es más fácil usar UseWaitCursor en el nivel de formulario 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 interfaz de usuario, debe utilizar la asincronía de un hilo diferente.
Mi enfoque sería hacer todos los cálculos en un trabajador de fondo.
Luego cambia el cursor de esta manera:
this.Cursor = Cursors.Wait;
Y en el evento final 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 he creado 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 a que termine. El control regresa a la persona que llama mientras espera. Así que la aplicación sigue siendo receptiva, incluso mientras el ícono 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 del formulario 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 obtener el cursor ocupado solo cuando estaba sobre un contenedor en particular en el formulario (pero no me esforcé mucho). Todos los controles tienen una propiedad UseWaitCursor, pero no parece tener ningún efecto en los controles Lo intenté (tal vez porque no estaban arriba)
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, la opinión de los demás es muy clara, pero me gustaría añadir algo, como sigue:
Cursor tempCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
//do Time-consuming Operations
Cursor.Current = tempCursor;
Sobre la base del enfoque anterior, mi enfoque preferido (ya que esta es una acción que se realiza con frecuencia) es envolver el código del cursor de espera en una clase de ayudante 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
}
Use esto con WPF:
Cursor = Cursors.Wait;
// Your Heavy work here
Cursor = Cursors.Arrow;
Puede utilizar Cursor.Current
. 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 hashing es realmente larga (MSDN define esto como más de 2 a 7 segundos), probablemente debería usar un indicador de retroalimentación visual que no sea el cursor para notificar al usuario el progreso. Para obtener un conjunto más detallado de pautas, consulte este artículo .
Editar:
Como @Am señaló, es posible que deba llamar a Application.DoEvents();
after Cursor.Current = Cursors.WaitCursor;
para asegurarse de que el reloj de arena se muestra realmente.