Cursor.Current vs. this.Cursor

¿Hay alguna diferencia entre Cursor.Current y this.Cursor (donde this es un WinForm) en .Net? Siempre he usado esto. this.Cursor y he tenido muy buena suerte con él, pero recientemente comencé a usar CodeRush y simplemente incrustó algún código en un bloque “Wait Cursor” y CodeRush usó la propiedad Cursor.Current . He visto en Internet y en el trabajo donde otros progtwigdores han tenido algunos problemas con el Cursor.Current . Propiedad Cursor.Current . Simplemente me hizo preguntarme si hay una diferencia en los dos. Gracias por adelantado.

Hice una pequeña prueba. Tengo dos formas de ganar. Hago clic en un botón en form1, establezco la propiedad Cursors.WaitCursor en Cursors.WaitCursor y luego muestro form2. El cursor no cambia en ninguna de las formas. Permanece Cursors.Default (puntero).

Si configuro this.Cursor a Cursors.WaitCursor en el botón haga clic en evento en form1 y muestre form2, el cursor de espera solo se muestra en form1 y el cursor predeterminado está en form2, que se espera. Entonces, todavía no sé qué hace Cursor.Current .

Windows envía la ventana que contiene el cursor del mouse al mensaje WM_SETCURSOR, dándole la oportunidad de cambiar la forma del cursor. Un control como TextBox toma ventaja de eso, cambiando el cursor en una I-bar. La propiedad Control.Cursor determina qué forma se usará.

La propiedad Cursor.Current cambia la forma directamente, sin esperar una respuesta WM_SETCURSOR. En la mayoría de los casos, es poco probable que esa forma sobreviva por mucho tiempo. Tan pronto como el usuario mueve el mouse, WM_SETCURSOR lo cambia a Control.Cursor.

La propiedad UseWaitCursor se agregó en .NET 2.0 para facilitar la visualización de un reloj de arena. Desafortunadamente, no funciona muy bien. Requiere un mensaje WM_SETCURSOR para cambiar la forma y eso no sucederá cuando establezca la propiedad en verdadero y luego haga algo que lleve un tiempo. Pruebe este código, por ejemplo:

 private void button1_Click(object sender, EventArgs e) { this.UseWaitCursor = true; System.Threading.Thread.Sleep(3000); this.UseWaitCursor = false; } 

El cursor nunca cambia. Para darle forma, necesitará usar Cursor.Current también. Aquí hay una pequeña clase de ayuda para hacerlo más fácil:

 using System; using System.Windows.Forms; public class HourGlass : IDisposable { public HourGlass() { Enabled = true; } public void Dispose() { Enabled = false; } public static bool Enabled { get { return Application.UseWaitCursor; } set { if (value == Application.UseWaitCursor) return; Application.UseWaitCursor = value; Form f = Form.ActiveForm; if (f != null && f.Handle != IntPtr.Zero) // Send WM_SETCURSOR SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); } } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); } 

Y úsalo así:

 private void button1_Click(object sender, EventArgs e) { using (new HourGlass()) { System.Threading.Thread.Sleep(3000); } } 

Creo que Cursor.Current es el cursor del mouse que se está usando actualmente (independientemente de dónde se encuentre en la pantalla), mientras que this.Cursor es el cursor en el que se establecerá cuando pase el mouse sobre la ventana.

En realidad, si desea usar HourGlass de otro hilo que le devolverá una excepción de cross-threading porque está tratando de acceder a f.Handle desde un hilo diferente al que se creó originalmente el formulario. Utilice GetForegroundWindow () en lugar de user32.dll.

 [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); 

y entonces

 public static bool Enabled { get { return Application.UseWaitCursor; } set { if (value == Application.UseWaitCursor) { return; } Application.UseWaitCursor = value; var handle = GetForegroundWindow(); SendMessage(handle, 0x20, handle, (IntPtr)1); } } 

this.Cursor es el cursor que se usará cuando el mouse esté sobre la ventana referida por this . Cursor.Current es el cursor actual del mouse, que puede ser diferente de este. this.Cursor si el mouse está sobre una ventana diferente.

He notado algo interesante sobre la configuración de cursores, por lo que me gustaría aclarar algunos malentendidos que yo tenía antes y espero que pueda ayudar a otros también:

Cuando intenta establecer el cursor de un formulario utilizando

this.cursor = Cursors.Waitcursor

en realidad configura el cursor para el control y no el formulario completo ya que el cursor es propiedad de la clase Control.

También, por supuesto, el cursor solo cambiará al cursor dado cuando el mouse esté realmente sobre el control real (explícitamente el área del formulario)

Como Hans Passant ya ha declarado que:

Windows envía la ventana que contiene el cursor del mouse el mensaje WM_SETCURSOR, dándole la oportunidad de cambiar la forma del cursor

No sé si Windows envía mensajes directamente a los controles o si el formulario transmite esos mensajes a sus controles secundarios en función de la posición del mouse, probablemente adivine el primer método desde que obtuve los mensajes sobrescribiendo WndProc del formulario. control, cuando estaba sobre el cuadro de texto, por ejemplo, el formulario no procesaba ningún mensaje. (Por favor, alguien aclare esto)

Básicamente mi sugerencia sería residir desde el uso de this.cursor también y apegarse a this.usewaitcursor, ya que eso cambia la propiedad del cursor a waitcursor para todos los controles secundarios.

El problema con esto también es el mismo que con el nivel de aplicación Application.usewaitcursor, mientras que no está sobre el formulario / formularios con el cursor, Windows no envía el mensaje WM_SETCURSOR, por lo que si inicia una operación sincrónica que requiere mucho tiempo antes de mover su desplazarse sobre el área del formulario, el formulario solo puede procesar dicho mensaje cuando finaliza la operación síncrona que consume mucho tiempo.

(No recomendaría ejecutar tareas que consumn tiempo en el hilo de la interfaz de usuario en absoluto, principalmente esto es lo que está causando el problema aquí)

Hice una pequeña mejora en la respuesta de Hans Passant, por lo que el reloj de arena se puede configurar en el nivel de aplicación o en el nivel de formulario, y también se evita la InvalidOperationException de las llamadas de operación con subprocesos cruzados:

 using System; using System.Windows.Forms; public class HourGlass : IDisposable { public static bool ApplicationEnabled { get{ return Application.UseWaitCursor; } set { Form activeFrom = Form.ActiveForm; if (activeFrom == null || ApplicationEnabled == value) return; if (ApplicationEnabled == value)return; Application.UseWaitCursor = (bool)value; if (activeFrom.InvokeRequired) { activeFrom.BeginInvoke(new Action(() => { if (activeFrom.Handle != IntPtr.Zero) SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR })); } else { if (activeFrom.Handle != IntPtr.Zero) SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR } } } private Form f; public HourGlass() { this.f = Form.ActiveForm; if (f == null) { throw new ArgumentException(); } Enabled = true; } public HourGlass(bool enabled) { this.f = Form.ActiveForm; if (f == null) { throw new ArgumentException(); } Enabled = enabled; } public HourGlass(Form f, bool enabled) { this.f = f; if (f == null) { throw new ArgumentException(); } Enabled = enabled; } public HourGlass(Form f) { this.f = f; if (f == null) { throw new ArgumentException(); } Enabled = true; } public void Dispose() { Enabled = false; } public bool Enabled { get { return f.UseWaitCursor; } set { if (f == null || Enabled == value) return; if (Application.UseWaitCursor == true && value == false) return; f.UseWaitCursor = (bool)value; if(f.InvokeRequired) { f.BeginInvoke(new Action(()=> { if (f.Handle != IntPtr.Zero) SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR })); } else { if (f.Handle != IntPtr.Zero) SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR } } } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); } 

Para usarlo en el nivel de aplicación:

 try { HourGlass.ApplicationEnabled = true; //time consuming synchronous task } finally { HourGlass.ApplicationEnabled = false; } 

Para usarlo en el nivel de formulario, puede usarlo para el formulario activo actual:

 using (new HourGlass()) { //time consuming synchronous task } 

o puede inicializar una variable local de la siguiente forma:

 public readonly HourGlass hourglass; public Form1() { InitializeComponent(); hourglass = new HourGlass(this, false); } 

y úsala más tarde en un bloque try catch finally

Esto funciona bien para mí cuando LongRunningOperation () está procesando mensajes.

 private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e) { this.Cursor = Cursors.WaitCursor; LongRunningOperation(); this.Cursor = Cursors.Arrow; } 

De VB.net VS 2012

 Windows.Forms.Cursor.Current = Cursors.Default