Wie kann ich den Cursor zum Wartecursor drehen lassen?

263

Ich habe eine C # -Anwendung, bei der sich Benutzer anmelden. Da der Hashing-Algorithmus teuer ist, dauert es eine Weile. Wie kann ich dem Benutzer den Wait / Busy Cursor (normalerweise die Sanduhr) anzeigen, um ihn wissen zu lassen, dass das Programm etwas tut?

Das Projekt ist in C #.

Malfist
quelle

Antworten:

451

Sie können verwenden Cursor.Current.

// Set cursor as hourglass
Cursor.Current = Cursors.WaitCursor;

// Execute your time-intensive hashing code here...

// Set cursor as default arrow
Cursor.Current = Cursors.Default;

Wenn der Hashing-Vorgang jedoch sehr langwierig ist (MSDN definiert dies als mehr als 2-7 Sekunden), sollten Sie wahrscheinlich eine andere visuelle Rückmeldungsanzeige als den Cursor verwenden, um den Benutzer über den Fortschritt zu informieren. Ausführlichere Richtlinien finden Sie in diesem Artikel .

Bearbeiten:
Wie @Am hervorhob, müssen Sie möglicherweise Application.DoEvents();nachher anrufen, Cursor.Current = Cursors.WaitCursor;um sicherzustellen, dass die Sanduhr tatsächlich angezeigt wird.

Krapfen
quelle
23
Dadurch muss der Cursor nicht geändert werden - wenn die Nachrichtenschleife während des zeitintensiven Codes nicht aufgerufen wird. Um es zu aktivieren, müssen Sie Application.DoEvents () hinzufügen . nach dem ersten Cursorsatz.
Amirshk
16
Sie möchten wahrscheinlich auch einen Versuch ... endgültig blockieren, nachdem Sie Current eingestellt haben (um sicherzustellen, dass Current auf Default zurückgesetzt wird).
TrueWill
7
Zu Ihrer Information, ich konnte das oben genannte nicht zum Laufen bringen, aber indem ich es in this.cursor = cursors.waitcursor änderte; es funktionierte.
Hans Rudel
4
Sanduhr wurde nicht angezeigt, wenn ich Application.DoEvents () nach Cursor.Current = Cursors.WaitCursor verwendet habe. Es funktionierte jedoch ohne Application.DoEvents (). Nicht sicher warum
Vbp
14
Es ist besser zu verwenden Application.UseWaitCursor = trueundApplication.UseWaitCursor = false
Gianpiero
169

Tatsächlich,

Cursor.Current = Cursors.WaitCursor;

Setzt den Wartecursor vorübergehend, stellt jedoch nicht sicher, dass der Wartecursor bis zum Ende Ihres Vorgangs angezeigt wird. Andere Programme oder Steuerelemente in Ihrem Programm können den Cursor leicht auf den Standardpfeil zurücksetzen, wie dies tatsächlich der Fall ist, wenn Sie die Maus bewegen, während der Vorgang noch ausgeführt wird.

Eine viel bessere Möglichkeit, den Wait-Cursor anzuzeigen, besteht darin, die UseWaitCursor-Eigenschaft in einem Formular auf true zu setzen:

form.UseWaitCursor = true;

Dadurch wird der Wartecursor für alle Steuerelemente im Formular angezeigt, bis Sie diese Eigenschaft auf false setzen. Wenn der Wartecursor auf Anwendungsebene angezeigt werden soll, sollten Sie Folgendes verwenden:

Application.UseWaitCursor = true;
Draganstankovic
quelle
Gut zu wissen. Ich habe versucht, dasselbe in WPF zu tun, und bin zu Cursor = Cursors.Wait und Cursor = Cursors.Arrow gekommen . Aber ich konnte den Cursor unter App
itsho
2
UseWaitCursor konnte unter Anwendung nicht gefunden werden!
Chandra Eskay
Ich habe festgestellt, dass beim Setzen von form.UseWaitCursor = false am Ende des Vorgangs der Cursor erst zurückgesetzt wird, wenn Sie die Maus bewegen oder klicken. OTOH, form.Cursor hat dieses Problem nicht. Ich konnte Cursor.Current überhaupt nicht zum Laufen bringen.
Stewart
39

Aufbauend auf dem vorherigen Ansatz besteht mein bevorzugter Ansatz (da dies eine häufig ausgeführte Aktion ist) darin, den Wartecursorcode in eine IDisposable-Hilfsklasse zu verpacken, damit er mit using () (einer Codezeile) verwendet werden kann, optionale Parameter zu übernehmen und auszuführen den Code darin, dann bereinigen (Cursor wiederherstellen).

public class CursorWait : IDisposable
{
    public CursorWait(bool appStarting = false, bool applicationCursor = false)
    {
        // Wait
        Cursor.Current = appStarting ? Cursors.AppStarting : Cursors.WaitCursor;
        if (applicationCursor) Application.UseWaitCursor = true;
    }

    public void Dispose()
    {
        // Reset
        Cursor.Current = Cursors.Default;
        Application.UseWaitCursor = false;
    }
}

Verwendungszweck:

using (new CursorWait())
{
    // Perform some code that shows cursor
}
mhapps
quelle
Hatte das nicht gesehen, aber ja ähnlichen Ansatz. Er sichert den aktuellen Cursor und stellt ihn dann wieder her. Dies kann hilfreich sein, wenn Sie den Cursor stark wechseln.
Mhapps
27

Es ist einfacher, UseWaitCursor auf Formular- oder Fensterebene zu verwenden. Ein typischer Anwendungsfall kann wie folgt aussehen:

    private void button1_Click(object sender, EventArgs e)
    {

        try
        {
            this.Enabled = false;//optional, better target a panel or specific controls
            this.UseWaitCursor = true;//from the Form/Window instance
            Application.DoEvents();//messages pumped to update controls
            //execute a lengthy blocking operation here, 
            //bla bla ....
        }
        finally
        {
            this.Enabled = true;//optional
            this.UseWaitCursor = false;
        }
    }

Für eine bessere Benutzeroberfläche sollten Sie Asynchrony aus einem anderen Thread verwenden.

dmihailescu
quelle
2
Dies sollte die AKZEPTIERTE Antwort sein. Es ist das einzige, das try-finally verwendet.
John Henckel
1
Habe mein Upvote, ich habe einen Versuch verpasst - endlich in meiner Implementierung
Jack
19

Mein Ansatz wäre es, alle Berechnungen in einem Hintergrundarbeiter durchzuführen.

Ändern Sie dann den Cursor wie folgt:

this.Cursor = Cursors.Wait;

Und im Finish-Ereignis des Threads stellen Sie den Cursor wieder her:

this.Cursor = Cursors.Default;

Beachten Sie, dass dies auch für bestimmte Steuerelemente möglich ist, sodass der Cursor nur dann die Sanduhr ist, wenn sich die Maus darüber befindet.

Amirshk
quelle
@Malfist: guter Ansatz :), dann musst du nur noch die Wiederherstellung im Endereignis platzieren und fertig.
Amirshk
4

OK, also habe ich eine statische asynchrone Methode erstellt. Dadurch wurde das Steuerelement deaktiviert, das die Aktion startet und den Anwendungscursor ändert. Es führt die Aktion als Aufgabe aus und wartet auf den Abschluss. Die Steuerung kehrt zum Anrufer zurück, während dieser wartet. Die Anwendung reagiert also auch dann, wenn sich das Besetztzeichen dreht.

async public static void LengthyOperation(Control control, Action action)
{
    try
    {
        control.Enabled = false;
        Application.UseWaitCursor = true;
        Task doWork = new Task(() => action(), TaskCreationOptions.LongRunning);
        Log.Info("Task Start");
        doWork.Start();
        Log.Info("Before Await");
        await doWork;
        Log.Info("After await");
    }
    finally
    {
        Log.Info("Finally");
        Application.UseWaitCursor = false;
        control.Enabled = true;
    }

Hier ist der Code aus dem Hauptformular

    private void btnSleep_Click(object sender, EventArgs e)
    {
        var control = sender as Control;
        if (control != null)
        {
            Log.Info("Launching lengthy operation...");
            CursorWait.LengthyOperation(control, () => DummyAction());
            Log.Info("...Lengthy operation launched.");
        }

    }

    private void DummyAction()
    {
        try
        {
            var _log = NLog.LogManager.GetLogger("TmpLogger");
            _log.Info("Action - Sleep");
            TimeSpan sleep = new TimeSpan(0, 0, 16);
            Thread.Sleep(sleep);
            _log.Info("Action - Wakeup");
        }
        finally
        {
        }
    }

Ich musste einen separaten Logger für die Dummy-Aktion verwenden (ich verwende Nlog) und mein Hauptlogger schreibt in die Benutzeroberfläche (ein Rich-Text-Feld). Ich konnte die Besetzt-Cursor-Show nur dann nicht abrufen, wenn sie sich über einem bestimmten Container im Formular befand (aber ich habe mich nicht sehr bemüht). Alle Steuerelemente haben eine UseWaitCursor-Eigenschaft, aber sie scheinen keine Auswirkungen auf die Steuerelemente zu haben Ich habe es versucht (vielleicht weil sie nicht oben waren?)

Hier ist das Hauptprotokoll, das die Ereignisse in der von uns erwarteten Reihenfolge zeigt:

16:51:33.1064 Launching lengthy operation...
16:51:33.1215 Task Start
16:51:33.1215 Before Await
16:51:33.1215 ...Lengthy operation launched.
16:51:49.1276 After await
16:51:49.1537 Finally
Darrel Lee
quelle
2

Mit der folgenden Klasse können Sie den Vorschlag von Donut "ausnahmesicher" machen.

using (new CursorHandler())
{
    // Execute your time-intensive hashing code here...
}

die Klasse CursorHandler

public class CursorHandler
    : IDisposable
{
    public CursorHandler(Cursor cursor = null)
    {
        _saved = Cursor.Current;
        Cursor.Current = cursor ?? Cursors.WaitCursor;
    }

    public void Dispose()
    {
        if (_saved != null)
        {
            Cursor.Current = _saved;
            _saved = null;
        }
    }

    private Cursor _saved;
}
Georg
quelle
2

Okey, die Sichtweise anderer Leute ist sehr klar, aber ich möchte einige hinzufügen, wie folgt:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;
Wenha
quelle
2

Für Windows Forms-Anwendungen kann eine optionale Deaktivierung eines UI-Steuerelements sehr nützlich sein. Mein Vorschlag sieht also so aus:

public class AppWaitCursor : IDisposable
{
    private readonly Control _eventControl;

    public AppWaitCursor(object eventSender = null)
    {
         _eventControl = eventSender as Control;
        if (_eventControl != null)
            _eventControl.Enabled = false;

        Application.UseWaitCursor = true;
        Application.DoEvents();
    }

    public void Dispose()
    {
        if (_eventControl != null)
            _eventControl.Enabled = true;

        Cursor.Current = Cursors.Default;
        Application.UseWaitCursor = false;
    }
}

Verwendungszweck:

private void UiControl_Click(object sender, EventArgs e)
{
    using (new AppWaitCursor(sender))
    {
        LongRunningCall();
    }
}
HEF
quelle
1

Verwenden Sie dies mit WPF:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;
Abdulrazzaq Alzayed
quelle