2008-11-19 6 views
52

Gibt es einen Unterschied zwischen Cursor.Current und this.Cursor (wobei this ein WinForm ist) in .Net? Ich habe immer this.Cursor verwendet und hatte sehr viel Glück damit, aber ich habe vor kurzem begonnen, CodeRush zu verwenden und nur etwas Code in einen "Wait Cursor" Block eingebettet und CodeRush verwendet die Cursor.Current Eigenschaft. Ich habe im Internet und auf der Arbeit gesehen, wo andere Programmierer Probleme mit der Cursor.Current Eigenschaft hatten. Es hat mich nur dazu gebracht mich zu fragen, ob es einen Unterschied in den beiden gibt. Danke im Voraus.Cursor.Current vs. this.Cursor

Ich habe einen kleinen Test gemacht. Ich habe zwei Winforms. Ich klicke auf eine Schaltfläche in form1, setze die Cursor.Current Eigenschaft auf Cursors.WaitCursor und zeige dann form2. Der Cursor ändert sich nicht in beiden Formularen. Es bleibt Cursors.Default (Zeiger) Cursor.

Wenn ich bis Cursors.WaitCursor in der Schaltfläche klicken Ereignis auf Form1 und Form2 anzeigen, zeigt der Wartezeitcursor nur auf Form1 und der Standardcursor ist auf Form2, die erwartet wird. Also, ich weiß immer noch nicht was Cursor.Current tut.

Antwort

84

Windows sendet das Fenster, das den Mauszeiger enthält, die WM_SETCURSOR-Nachricht, die es ermöglicht, die Cursor-Form zu ändern. Ein Steuerelement wie TextBox nutzt dies, indem es den Cursor in einen I-Balken umwandelt. Die Control.Cursor-Eigenschaft bestimmt, welche Form verwendet wird.

Die Cursor.Current-Eigenschaft ändert die Form direkt, ohne auf eine WM_SETCURSOR-Antwort zu warten. In den meisten Fällen wird diese Form wahrscheinlich nicht lange überleben. Sobald der Benutzer die Maus bewegt, ändert WM_SETCURSOR es zurück in Control.Cursor.

Die UseWaitCursor-Eigenschaft wurde in .NET 2.0 hinzugefügt, um die Anzeige einer Sanduhr zu erleichtern. Leider funktioniert es nicht sehr gut. Es erfordert eine WM_SETCURSOR-Nachricht, um die Form zu ändern, und das wird nicht passieren, wenn Sie die Eigenschaft auf true festlegen und dann etwas tun, das eine Weile dauert. Versuchen Sie diesen Code zum Beispiel:

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

Der Cursor ändert sich nie. Um das in Form zu bringen, müssen Sie auch Cursor.Current verwenden. Hier ist ein kleiner Helfer Klasse, um es einfach:

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

und verwenden Sie es wie folgt aus:

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

Ich stieß auf einen Fall bei der Kombination mit einem Begrüßungsbildschirm, der eine InvalidOperationException verursachen würde - "Cross-Thread-Operation nicht gültig". Das Hinzufügen eines! F.InvokeRequired zwischen f! = Null und f.Handle! = Null löste das Problem. –

+0

Das funktioniert gut für mich, aber laut ReSharper, "Ausdruck ist immer wahr" in dieser Zeile: if (f! = Null && f.Handle! = Null) // Senden WM_SETCURSOR –

+0

Dies ist eine tolle Helfer-Klasse. Arbeitete, als nichts anderes tat. – KeithS

10

Ich glaube, dass Cursor.Current der Mauszeiger derzeit verwendet wird (unabhängig davon, wo es auf dem Bildschirm ist), während this.Cursor der Cursor ist, auf den festgelegt wird, wenn die Maus über Ihr Fenster übergibt.

+0

Dies scheint ** nicht wahr **. Ich habe eine Beispielanwendung erstellt, um dies zu überprüfen und es scheint, dass "System.Windows.Cursors.Current" nur aktualisiert wird, wenn die Cursoränderung mit dem Anwendungsfenster verknüpft ist. –

+0

Der Unterschied ist, dass 'this.Cursor' nicht aktualisiert wird, auch wenn der Cursor über einem untergeordneten Steuerelement eines Fensters oder über den nicht-Client-Bereich des Fensters ist. Entschuldigung für zwei nachfolgende Kommentare, die Bearbeitungszeit für die erste wurde beendet. –

5

this.Cursor ist der Cursor, der verwendet wird, wenn sich die Maus über dem Fenster befindet, auf das sich this bezieht. Cursor.Current ist der aktuelle Mauszeiger, der sich möglicherweise von this.Cursor unterscheidet, wenn sich die Maus über einem anderen Fenster befindet.

6

Eigentlich, wenn Sie Hourglass von einem anderen Thread verwenden möchten, dass Sie geben zurück Verkanten Ausnahme, weil Sie versuchen, auf f.Handle aus einem anderen Thread zuzugreifen, als das Formular ursprünglich erstellt wurde. Verwenden Sie stattdessen GetForegroundWindow() von user32.dll.

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

und dann

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

Entschuldigung! Ich habe einen Laufzeitfehler, aber es war meine Schuld! Ich habe die '[DllImport (" user32.dll ")]' oben auf der Methodensignatur verpasst. Es funktioniert super! –

+0

Dies ist die einzige Lösung, die für meine Cross-Threading-Situation zuverlässig funktioniert hat. –

0

Dies funktioniert gut für mich, wenn die LongRunningOperation() wird die Verarbeitung von Nachrichten.

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

Was passiert, wenn die LongRunningOperation fehlschlägt? Sollte zumindest einen Versuch/endlich hier haben. Auch wenn der Cursor kein Pfeil ist, mit dem z.B. Sie sind in einer TextBox? –

0

Von VB.net VS 2012

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

Ich bin mir nicht sicher, wie das die Hauptfrage beantwortet. Könntest Du das erläutern? –

+0

Dies ist nur der Weg, um auf den aktuellen Cursor von der Klasse Cursor, nicht von der Eigenschaft Cursor zuzugreifen. – Elian

2

Ich habe eine interessante Sache über die Einstellung Cursor bemerkt haben, so würde Ich mag einige Vorbehalte ausräumen, dass ich mich vor hatte, und ich hoffe, dass es anderen helfen kann zu:

Wenn Sie versuchen, eine Form des Cursors zu setzen, indem

this.cursor = Cursors.Waitcursor

mit 0

Sie tatsächlich den Cursor für das Steuerelement und nicht das gesamte Formular seit Cursor ist die Eigenschaft der Klasse Control.

auch natürlich der Cursor nur auf den angegebenen Cursor geändert werden, wenn die Maus tatsächlich über die eigentliche Steuerung ist (explizit den Bereich der Form)

Wie Hans Passant hat bereits erklärt, dass:

Windows sendet das Fenster, das sich der Mauszeiger die WM_SETCURSOR Nachricht enthält, ist es eine Möglichkeit zu geben, den Cursor Form

I k nicht zu ändern Wenn Windows jetzt Nachrichten direkt an Steuerelemente sendet oder wenn das Formular diese Nachrichten an seine untergeordneten Steuerelemente basierend auf der Mausposition weiterleitet, würde ich höchstwahrscheinlich die erste Methode erraten, seit ich die Nachrichten mit überschreibendem WndProc des Formularsteuerelements abgerufen habe, wenn ich war über dem Textfeld zum Beispiel, das Formular hat keine Nachrichten verarbeitet. (bitte jemand gibt Klarheit darüber)

Grundsätzlich wäre mein Vorschlag, auch davon zu leben, this.cursor zu verwenden und bei this.usewaitcursor zu bleiben, da das die Cursor-Eigenschaft in waitcursor für alle untergeordneten Steuerelemente ändert.

Das Problem damit ist auch das gleiche wie mit der Anwendungsebene Application.usewaitcursor, während Sie nicht über das Formular/Formulare mit Ihrem Cursor keine WM_SETCURSOR Nachricht von Windows gesendet wird, so wenn Sie eine zeitraubende synchrone starten Bevor Sie den Mauszeiger über den Bereich des Formulars bewegen, kann das Formular diese Nachricht nur bearbeiten, wenn der zeitintensive synchrone Vorgang abgeschlossen ist.

(ich würde nicht zu zeitraubenden Aufgaben im UI-Thread schlagen laufe, vor allem das ist, was das Problem hier verursacht)

ich eine kleine Verbesserung gegenüber Hans Passant Antwort gemacht, so dass die Sanduhr kann entweder setzt auf Anwendungsebene oder Formularebene, auch InvalidOperationException aus Kreuzgewinde Betrieb zu vermeiden ruft:

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

es nutzen zu können, auf Anwendungsebene:

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

Denn es auf Formularebene verwenden Sie entweder für die aktuelle aktive Form verwenden:

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

oder Sie können eine lokale Variable in der Form wie folgt initialisieren:

public readonly HourGlass hourglass; 

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

und verwenden Sie es später in ein Versuch zu fangen schließlich Block