Cursor.Current vs. this.Cursor

Y a-t-il une différence entre Cursor.Current et this.Cursor (où this s’agit d’un WinForm) dans .Net? J’ai toujours utilisé this.Cursor et j’ai eu beaucoup de chance avec cela, mais j’ai récemment commencé à utiliser CodeRush et j’ai juste incorporé du code dans un bloc “Wait Cursor” et CodeRush utilisait la propriété Cursor.Current . J’ai vu sur Internet et au travail où d’autres programmeurs ont eu des problèmes avec la propriété Cursor.Current . Cela m’a amené à me demander s’il y avait une différence entre les deux. Merci d’avance.

J’ai fait un petit test. J’ai deux winforms. Je clique sur un bouton sur form1, définissez la propriété Cursors.WaitCursor sur Cursors.WaitCursor , puis affichez form2. Le curseur ne change sur aucun des formulaires. Il rest le Cursors.Default (pointeur).

Si je this.Cursor à Cursors.WaitCursor dans l’événement click du bouton sur form1 et affiche form2, le curseur d’attente ne s’affiche que sur form1 et le curseur par défaut est sur form2 attendu. Donc, je ne sais toujours pas ce que fait Cursor.Current .

Windows envoie la fenêtre contenant le curseur de la souris au message WM_SETCURSOR, ce qui lui permet de modifier la forme du curseur. Un contrôle comme TextBox en profite pour changer le curseur en I-Bar. La propriété Control.Cursor détermine la forme à utiliser.

La propriété Cursor.Current modifie directement la forme sans attendre une réponse WM_SETCURSOR. Dans la plupart des cas, cette forme est peu susceptible de survivre longtemps. Dès que l’utilisateur déplace la souris, WM_SETCURSOR le ramène à Control.Cursor.

La propriété UseWaitCursor a été ajoutée dans .NET 2.0 pour faciliter l’affichage d’un sablier. Malheureusement, cela ne fonctionne pas très bien. Il faut un message WM_SETCURSOR pour modifier la forme et cela ne se produira pas lorsque vous définissez la propriété sur true et que vous faites quelque chose qui prend du temps. Essayez ce code par exemple:

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

Le curseur ne change jamais. Pour le mettre en forme, vous devez également utiliser Cursor.Current. Voici un petit cours d’aide pour vous faciliter la tâche:

 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); } 

Et l’utiliser comme ça:

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

Je crois que Cursor.Current est le curseur de la souris actuellement utilisé (quel que soit l’endroit où il se trouve à l’écran), alors que this.Cursor est le curseur sur lequel il sera défini, lorsque la souris passe sur votre fenêtre.

En fait, si vous souhaitez utiliser HourGlass à partir d’un autre thread qui vous renverra une exception de threading croisé parce que vous essayez d’accéder à f.Handle à partir de threads différents de ceux créés à l’origine. Utilisez GetForegroundWindow () à la place de user32.dll.

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

et alors

 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 est le curseur qui sera utilisé lorsque la souris survole la fenêtre à laquelle elle fait référence. Cursor.Current est le curseur actuel de la souris, qui peut être différent de celui- this.Cursor si la souris se trouve sur une autre fenêtre.

J’ai remarqué une chose intéressante à propos de la configuration des curseurs, je voudrais donc effacer certains malentendus que j’avais déjà et j’espère que cela pourrait aider les autres aussi:

Lorsque vous essayez de définir le curseur d’un formulaire en utilisant

this.cursor = Cursors.Waitcursor

En fait, vous définissez le curseur pour le contrôle et non le formulaire entier, car le curseur est la propriété de la classe Control.

Bien entendu, le curseur ne sera remplacé que par le curseur lorsque la souris passe réellement le contrôle réel (explicitement la zone du formulaire)

Comme Hans Passant l’a déjà déclaré:

Windows envoie la fenêtre contenant le curseur de la souris au message WM_SETCURSOR, ce qui lui permet de changer la forme du curseur

Je ne sais pas si Windows envoie les messages directement aux contrôles ou si le formulaire transmet ces messages à ses contrôles enfants en fonction de la position de la souris, je devinerais probablement la première méthode depuis laquelle j’ai récupéré les messages avec WndProc de la forme contrôle, lorsque j’étais sur la zone de texte par exemple, le formulaire n’a traité aucun message. (s’il vous plaît quelqu’un donner des précisions à ce sujet)

Fondamentalement, ma suggestion serait de résider en utilisant this.cursor et de coller à this.usewaitcursor, car cela change la propriété du curseur en waitcursor pour tous les contrôles enfants.

Le problème est le même avec le niveau d’application Application.usewaitcursor, alors que vous n’avez pas dépassé la forme / les formulaires avec votre curseur, aucun message WM_SETCURSOR n’est envoyé par Windows. Si vous passez la souris sur la zone du formulaire, le formulaire ne peut traiter ce message que lorsque l’opération synchrone prend du temps.

(Je ne suggérerais pas d’exécuter du temps dans le thread de l’interface utilisateur, mais c’est ce qui cause le problème ici)

J’ai apporté une petite amélioration à la réponse de Hans Passant, de sorte que le sablier puisse être défini au niveau de l’application ou au niveau du formulaire, évitant également les InvalidOperationException des appels d’opérations croisées:

 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); } 

Pour l’utiliser au niveau de l’application:

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

Pour l’utiliser au niveau du formulaire, vous pouvez soit utiliser pour le formulaire actif actuel:

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

ou vous pouvez initialiser une variable locale dans le formulaire comme ceci:

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

et l’utiliser plus tard dans un essai

Cela fonctionne très bien pour moi lorsque LongRunningOperation () traite des messages.

 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