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
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. –
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 –
Dies ist eine tolle Helfer-Klasse. Arbeitete, als nichts anderes tat. – KeithS