web-dev-qa-db-fra.com

Comment puis-je faire en sorte que le curseur se tourne vers le curseur d'attente?

J'ai une application C # à laquelle les utilisateurs se connectent, et comme l'algorithme de hachage est coûteux, cela prend un peu de temps. Comment puis-je afficher le curseur d'attente/occupé (généralement le sablier) à l'utilisateur pour lui faire savoir que le programme fait quelque chose?

Le projet est en C #.

214
Malfist

Vous pouvez utiliser 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;

Toutefois, si l'opération de hachage est vraiment longue (MSDN le définit comme supérieur à 2 à 7 secondes), vous devez probablement utiliser un indicateur de retour visuel autre que le curseur pour informer l'utilisateur de la progression. Pour un ensemble plus détaillé de directives, voir cet article .

Modifier:
Comme @Am l’a souligné, vous devrez peut-être appeler Application.DoEvents(); après Cursor.Current = Cursors.WaitCursor; pour vous assurer que le sablier est bien affiché. 

382
Donut

Réellement, 

Cursor.Current = Cursors.WaitCursor;

temporairement définit le curseur d’attente, mais ne garantit pas que le curseur d’attente s’affiche jusqu’à la fin de votre opération. D'autres programmes ou contrôles au sein de votre programme peuvent facilement réinitialiser le curseur sur la flèche par défaut, comme cela se produit lorsque vous déplacez la souris alors que l'opération est toujours en cours d'exécution. 

Un meilleur moyen d’afficher le curseur d’attente consiste à définir la propriété UseWaitCursor dans un formulaire sur true:

form.UseWaitCursor = true;

Cela affichera le curseur d'attente pour tous les contrôles du formulaire jusqu'à ce que vous définissiez cette propriété à false . Si vous souhaitez que le curseur d'attente s'affiche au niveau de l'application, vous devez utiliser:

Application.UseWaitCursor = true;
149
draganstankovic

S'appuyant sur la précédente, mon approche préférée (puisqu'il s'agit d'une action fréquemment exécutée) consiste à envelopper le code du curseur d'attente dans une classe d'assistance IDisposable afin de pouvoir l'utiliser avec using () (une ligne de code), prendre des paramètres le code à l'intérieur, puis nettoyez (restaurez le curseur) après.

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

Usage:

using (new CursorWait())
{
    // Perform some code that shows cursor
}
30
mhapps

Il est plus facile d'utiliser UseWaitCursor au niveau du formulaire ou de la fenêtre . Un cas d'utilisation typique peut ressembler à celui ci-dessous:

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

Pour une meilleure expérience d’interface utilisateur, vous devez utiliser Asynchrony à partir d’un autre thread.

22
dmihailescu

Mon approche serait de faire tous les calculs avec un travailleur de fond.

Puis changez le curseur comme ceci:

this.Cursor = Cursors.Wait;

Et dans l'événement de fin du fil, restaurez le curseur:

this.Cursor = Cursors.Default;

Notez que ceci peut également être fait pour des contrôles spécifiques, ainsi le curseur ne sera le sablier que lorsque la souris sera au-dessus d'eux.

17
Amirshk

OK, j'ai donc créé une méthode async statique. Cela a désactivé le contrôle qui lance l'action et modifie le curseur de l'application. Il exécute l'action en tant que tâche et attend la fin. Le contrôle revient à l'appelant pendant qu'il attend. L’application reste donc réactive, même pendant que l’icône occupée tourne.

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

Voici le code de la forme principale

    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
        {
        }
    }

J'ai dû utiliser un enregistreur distinct pour l'action fictive (j'utilise Nlog) et mon enregistreur principal écrit dans l'interface utilisateur (zone de texte enrichi). Je n'ai pas réussi à afficher le curseur occupé uniquement lorsque vous avez survolé un conteneur particulier du formulaire (mais je n'ai pas essayé très fort.) Tous les contrôles ont une propriété UseWaitCursor, mais cela ne semble pas avoir d'effet sur les contrôles J'ai essayé (peut-être parce qu'ils n'étaient pas au top?)

Voici le journal principal, qui montre ce qui se passe dans l'ordre prévu:

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
4
Darrel Lee

Okey, le point de vue des autres est très clair, mais j'aimerais en ajouter, comme suit:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;
2
wenha

Pour les applications Windows Forms, la désactivation facultative d'un UI-Control peut être très utile. Donc, ma suggestion ressemble à ceci:

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

Usage:

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

Avec la classe ci-dessous, vous pouvez faire la suggestion de Donut "exception sûre".

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

la classe 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;
}
1
Georg

Utilisez ceci avec WPF:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;
0
Alzayed