web-dev-qa-db-fra.com

Cursor.Current contre this.Cursor

Y a-t-il une différence entre Cursor.Current et this.Cursor (où this est un WinForm) dans .Net? J'ai toujours utilisé this.Cursor et j'ai eu beaucoup de chance avec, mais j'ai récemment commencé à utiliser CodeRush et j'ai juste intégré du code dans un bloc "Wait Cursor" et CodeRush a utilisé le Cursor.Current propriété. J'ai vu sur Internet et au travail où d'autres programmeurs ont eu des problèmes avec le Cursor.Current propriété. 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 formes de victoire. Je clique sur un bouton sur form1, définit le Cursor.Current propriété à Cursors.WaitCursor puis affichez form2. Le curseur ne change sur aucun des deux formulaires. Il reste Cursors.Default (pointeur) curseur.

Si je mets this.Cursor à Cursors.WaitCursor dans l'événement click click sur form1 et show form2, le curseur d'attente ne s'affiche que sur form1 et le curseur par défaut se trouve sur form2 qui est attendu. Donc, je ne sais toujours pas ce que Cursor.Current Est-ce que.

54
Keith

Windows envoie à la fenêtre contenant le curseur de la souris le message WM_SETCURSOR, lui donnant la possibilité de modifier la forme du curseur. Un contrôle comme TextBox profite de cela, en changeant le curseur en une barre en I. La propriété Control.Cursor détermine la forme qui sera utilisée.

La propriété Cursor.Current modifie directement la forme, sans attendre une réponse WM_SETCURSOR. Dans la plupart des cas, cette forme a peu de chances de survivre longtemps. Dès que l'utilisateur déplace la souris, WM_SETCURSOR la ​​redéfinit en 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 nécessite 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 effectuez ensuite quelque chose qui prend un certain 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 mettre cela en forme, vous devrez également utiliser Cursor.Current. Voici une petite classe 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 utilisez-le comme ceci:

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

Je crois que Cursor.Current est le curseur de la souris actuellement utilisé (quel que soit son emplacement sur l'écran), tandis que this.Cursor est le curseur sur lequel il sera défini, lorsque la souris passera sur votre fenêtre.

10
James Curran

En fait, si vous souhaitez utiliser HourGlass à partir d'un autre thread qui vous renverra une exception de cross-threading parce que vous essayez d'accéder à f.Handle à partir d'un thread différent de celui du formulaire créé à l'origine. Utilisez GetForegroundWindow () à la place à partir de user32.dll.

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

puis

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);
    }
}
6
wałdis iljuczonok

this.Cursor est le curseur qui sera utilisé lorsque la souris survolera la fenêtre désignée par this. Cursor.Current est le curseur actuel de la souris, qui peut être différent de this.Cursor si la souris se trouve sur une autre fenêtre.

6
Joel Coehoorn

J'ai remarqué une chose intéressante à propos de la configuration des curseurs, donc je voudrais dissiper certains malentendus que j'avais moi-même auparavant et j'espère que cela pourra aussi aider les autres:

Lorsque vous essayez de définir le curseur d'un formulaire à l'aide de

this.cursor = Cursors.Waitcursor

vous définissez réellement le curseur pour le contrôle et non la forme entière puisque le curseur est la propriété de la classe Control.

De plus, bien sûr, le curseur ne sera remplacé par le curseur donné que lorsque la souris est réellement sur 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 le message WM_SETCURSOR, lui donnant la possibilité de modifier la forme du curseur

Je ne sais pas si Windows envoie des messages directement aux contrôles ou si le formulaire relaie ces messages à ses contrôles enfants en fonction de la position de la souris, je devinerais probablement la première méthode depuis que j'ai récupéré les messages avec le WndProc prioritaire du formulaire contrôle, lorsque j'étais sur la zone de texte par exemple, le formulaire ne traitait aucun message. (s'il vous plaît quelqu'un donne de la clarté à ce sujet)

Fondamentalement, ma suggestion serait de ne pas utiliser this.cursor également et de s'en tenir à this.usewaitcursor, car cela change la propriété du curseur en waitcursor pour tous les contrôles enfants.

Le problème avec cela est également le même qu'avec le niveau d'application Application.usewaitcursor, alors que vous n'êtes pas sur le formulaire/formulaires avec votre curseur, aucun message WM_SETCURSOR n'est envoyé par Windows, donc si vous démarrez une opération synchrone longue avant de déplacer votre Passez la souris sur la zone du formulaire, le formulaire ne peut traiter ce message que lorsque l'opération synchrone longue prend fin.

(Je ne suggérerais pas du tout d'exécuter des tâches chronophages dans le thread d'interface utilisateur, c'est principalement 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 peut être défini au niveau de l'application ou au niveau du formulaire, évitant également 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 sous la forme suivante:

public readonly HourGlass hourglass;

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

et l'utiliser plus tard dans un bloc catch catch enfin

2
Attila Horváth

De VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default
0
Elian

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;
}
0
bootsn