Comment puis-je mettre à jour la ligne actuelle dans une application de console Windows C #?

507

Lors de la création d'une application console Windows en C #, est-il possible d'écrire sur la console sans avoir à étendre une ligne actuelle ou à passer à une nouvelle ligne? Par exemple, si je veux afficher un pourcentage représentant la fin d'un processus, je voudrais juste mettre à jour la valeur sur la même ligne que le curseur, et ne pas avoir à mettre chaque pourcentage sur une nouvelle ligne.

Cela peut-il être fait avec une application console C # "standard"?

IVR Avenger
la source
Si vous êtes VRAIMENT intéressé par des interfaces de ligne de commande sympas, vous devriez vérifier curses / ncurses.
Charles Addis
@CharlesAddis mais les curses / ncurses ne fonctionnent-ils qu'en C ++?
Xam

Réponses:

783

Si vous imprimez uniquement "\r"sur la console, le curseur revient au début de la ligne en cours et vous pouvez ensuite la réécrire. Cela devrait faire l'affaire:

for(int i = 0; i < 100; ++i)
{
    Console.Write("\r{0}%   ", i);
}

Remarquez les quelques espaces après le numéro pour vous assurer que tout ce qui était auparavant est effacé.
Notez également l'utilisation de Write()au lieu de WriteLine()car vous ne voulez pas ajouter de "\ n" à la fin de la ligne.

shoosh
la source
7
pour (int i = 0; i <= 100; ++ i) ira à 100%
Nicolas Tyler
13
Comment gérez-vous lorsque l'écriture précédente était plus longue que la nouvelle écriture? Existe-t-il un moyen d'obtenir la largeur de la console et de garnir la ligne avec des espaces, peut-être?
Drew Chapin
6
@druciferre Du haut de ma tête, je peux penser à deux réponses à votre question. Ils impliquent tous deux d'enregistrer la sortie actuelle sous forme de chaîne en premier et de la remplir avec une quantité définie de caractères comme ceci: Console.Write ("\ r {0}", strOutput.PadRight (nPaddingCount, '')); Le "nPaddingCount" peut être un nombre que vous définissez vous-même ou vous pouvez suivre la sortie précédente et définir nPaddingCount comme la différence de longueur entre la sortie précédente et actuelle plus la longueur de sortie actuelle. Si nPaddingCount est négatif, vous ne devrez pas utiliser PadRight à moins que vous ne fassiez abs (prev.len - curr.len).
John Odom du
1
@malgm Code bien organisé. Si l'un des douze threads pouvait écrire sur la console à tout moment, cela vous poserait des problèmes, que vous écriviez de nouvelles lignes ou non.
Mark
2
@JohnOdom vous avez seulement besoin de conserver la longueur de sortie précédente (non rembourrée), puis de l'introduire comme premier argument dans PadRight(enregistrer la chaîne non rembourrée, ou la longueur, d'abord, bien sûr).
Jesper Matthiesen
254

Vous pouvez utiliser Console.SetCursorPositionpour définir la position du curseur, puis écrire à la position actuelle.

Voici un exemple montrant un simple "spinner":

static void Main(string[] args)
{
    var spin = new ConsoleSpinner();
    Console.Write("Working....");
    while (true) 
    {
        spin.Turn();
    }
}

public class ConsoleSpinner
{
    int counter;

    public void Turn()
    {
        counter++;        
        switch (counter % 4)
        {
            case 0: Console.Write("/"); counter = 0; break;
            case 1: Console.Write("-"); break;
            case 2: Console.Write("\\"); break;
            case 3: Console.Write("|"); break;
        }
        Thread.Sleep(100);
        Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
    }
}

Notez que vous devrez vous assurer d'écraser toute sortie existante avec une nouvelle sortie ou des blancs.

Mise à jour: Comme il a été critiqué que l'exemple ne déplace le curseur que d'un caractère en arrière, j'ajouterai ceci pour clarification: En utilisant, SetCursorPositionvous pouvez placer le curseur à n'importe quelle position dans la fenêtre de la console.

Console.SetCursorPosition(0, Console.CursorTop);

placera le curseur au début de la ligne actuelle (ou vous pouvez utiliser Console.CursorLeft = 0directement).

Dirk Vollmar
la source
8
Le problème peut être résolu en utilisant \ r, mais en utilisant SetCursorPosition(ou CursorLeft) permet plus de flexibilité, par exemple en n'écrivant pas au début de la ligne, en remontant dans la fenêtre, etc. c'est donc une approche plus générale qui peut être utilisée par exemple pour sortir barres de progression personnalisées ou graphique ASCII.
Dirk Vollmar
14
+1 pour être verbeux et aller au-delà de l'appel du devoir. Bon truc merci.
Copas
1
+1 pour montrer une manière différente de le faire. Tout le monde a montré \ r, et si l'OP met simplement à jour un pourcentage, il peut simplement mettre à jour la valeur sans avoir à réécrire toute la ligne. L'OP n'a jamais réellement dit qu'il voulait passer au début de la ligne, juste qu'il voulait mettre à jour quelque chose sur la même ligne que le curseur.
Andy
1
La flexibilité supplémentaire de SetCursorPosition se fait au prix d'un peu de vitesse et d'un scintillement notable du curseur si la boucle est suffisamment longue pour que l'utilisateur le remarque. Voir mon commentaire de test ci-dessous.
Kevin
5
Confirmez également que la longueur de la ligne n'entraîne pas le retour à la ligne suivante de la console ou que vous pouvez de toute façon rencontrer des problèmes avec le contenu descendant dans la fenêtre de la console.
Mandrake
84

Jusqu'à présent, nous avons trois alternatives concurrentes sur la façon de procéder:

Console.Write("\r{0}   ", value);                      // Option 1: carriage return
Console.Write("\b\b\b\b\b{0}", value);                 // Option 2: backspace
{                                                      // Option 3 in two parts:
    Console.SetCursorPosition(0, Console.CursorTop);   // - Move cursor
    Console.Write(value);                              // - Rewrite
}

J'ai toujours utilisé Console.CursorLeft = 0une variante de la troisième option, j'ai donc décidé de faire quelques tests. Voici le code que j'ai utilisé:

public static void CursorTest()
{
    int testsize = 1000000;

    Console.WriteLine("Testing cursor position");
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < testsize; i++)
    {
        Console.Write("\rCounting: {0}     ", i);
    }
    sw.Stop();
    Console.WriteLine("\nTime using \\r: {0}", sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    int top = Console.CursorTop;
    for (int i = 0; i < testsize; i++)
    {
        Console.SetCursorPosition(0, top);        
        Console.Write("Counting: {0}     ", i);
    }
    sw.Stop();
    Console.WriteLine("\nTime using CursorLeft: {0}", sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    Console.Write("Counting:          ");
    for (int i = 0; i < testsize; i++)
    {        
        Console.Write("\b\b\b\b\b\b\b\b{0,8}", i);
    }

    sw.Stop();
    Console.WriteLine("\nTime using \\b: {0}", sw.ElapsedMilliseconds);
}

Sur ma machine, j'obtiens les résultats suivants:

  • Espaces arrière: 25,0 secondes
  • Retours chariot: 28,7 secondes
  • SetCursorPosition: 49,7 secondes

De plus, cela a SetCursorPositionprovoqué un scintillement notable que je n'ai observé avec aucune des alternatives. Donc, la morale est d' utiliser des backspaces ou des retours chariot lorsque cela est possible , et merci de m'avoir enseigné un moyen plus rapide de le faire, SO!


Mise à jour : dans les commentaires, Joel suggère que SetCursorPosition est constant par rapport à la distance parcourue alors que les autres méthodes sont linéaires. Des tests supplémentaires confirment que c'est le cas, mais le temps constant et lent est toujours lent. Dans mes tests, l'écriture d'une longue chaîne d'espaces arrière sur la console est plus rapide que SetCursorPosition jusqu'à environ 60 caractères. Ainsi, le retour arrière est plus rapide pour remplacer des parties de la ligne de moins de 60 caractères (ou plus), et il ne scintille pas, donc je vais m'en tenir à mon approbation initiale de \ b sur \ r et SetCursorPosition.

Kevin
la source
4
L'efficacité de l'opération en question ne devrait vraiment pas avoir d'importance. Tout cela devrait se produire trop rapidement pour que l'utilisateur le remarque. La microptimisation inutile est mauvaise.
Malfist
@Malfist: Selon la longueur de la boucle, l'utilisateur peut ou non le remarquer. Comme je l'ai ajouté dans l'édition ci-dessus (avant de voir votre commentaire), SetCursorPosition a introduit le scintillement et prend presque deux fois plus de temps que les autres options.
Kevin
1
Je suis d'accord qu'il s'agit d'une micro-optimisation (l'exécuter un million de fois et prendre 50 secondes, c'est encore très peu de temps), +1 pour les résultats, et cela pourrait certainement être très utile à savoir.
Andy
6
L'indice de référence est fondamentalement défectueux. Il est possible que le temps SetCursorPosition () soit le même quel que soit le déplacement du curseur, tandis que les autres options varient en fonction du nombre de caractères que la console doit traiter.
Joel Coehoorn
1
Ceci est un très bon résumé des différentes options disponibles. Cependant, je vois également un scintillement lors de l'utilisation de \ r. Avec \ b il n'y a évidemment pas de scintillement car le texte du correctif ("Counting:") n'est pas réécrit. Vous obtiendrez également un scintillement si vous ajoutez des \ b supplémentaires et réécrivez le texte du correctif comme cela se produit avec \ b et SetCursorPosition. Concernant la remarque de Joel: Joel a fondamentalement raison, cependant \ r surclassera toujours SetCursorPosition sur de très longues lignes, mais la différence diminue.
Dirk Vollmar
27

Vous pouvez utiliser la séquence d'échappement \ b (retour arrière) pour sauvegarder un nombre particulier de caractères sur la ligne actuelle. Cela déplace simplement l'emplacement actuel, il ne supprime pas les caractères.

Par exemple:

string line="";

for(int i=0; i<100; i++)
{
    string backup=new string('\b',line.Length);
    Console.Write(backup);
    line=string.Format("{0}%",i);
    Console.Write(line);
}

Ici, la ligne est la ligne de pourcentage à écrire sur la console. L'astuce consiste à générer le nombre correct de \ b caractères pour la sortie précédente.

L'avantage de ceci sur l' approche \ r est que si fonctionne même si votre pourcentage de sortie n'est pas au début de la ligne.

Sean
la source
1
+1, cela s'avère être la méthode la plus rapide présentée (voir mon commentaire de test ci-dessous)
Kevin
19

\rest utilisé pour ces scénarios.
\r représente un retour chariot ce qui signifie que le curseur revient au début de la ligne.
C'est pourquoi Windows utilise \n\rcomme nouveau marqueur de ligne.
\nvous fait descendre d'une ligne et \rvous ramène au début de la ligne.

Malfist
la source
22
Sauf que c'est en fait \ r \ n.
Joel Mueller
14

Je devais juste jouer avec la ConsoleSpinnerclasse de la divo . Le mien est loin d'être aussi concis, mais cela ne me convient pas que les utilisateurs de cette classe doivent écrire leur propre while(true)boucle. Je vis pour une expérience plus semblable à celle-ci:

static void Main(string[] args)
{
    Console.Write("Working....");
    ConsoleSpinner spin = new ConsoleSpinner();
    spin.Start();

    // Do some work...

    spin.Stop(); 
}

Et je l'ai réalisé avec le code ci-dessous. Comme je ne veux pas que ma Start()méthode se bloque, je ne veux pas que l'utilisateur ait à se soucier d'écrire une while(spinFlag)boucle semblable à celle-ci, et je veux autoriser plusieurs filateurs en même temps, j'ai dû générer un thread séparé pour gérer le filage. Et cela signifie que le code doit être beaucoup plus compliqué.

De plus, je n'ai pas fait beaucoup de multi-threading, il est donc possible (probablement même) que j'aie laissé un ou trois bogues subtils. Mais cela semble assez bien fonctionner jusqu'à présent:

public class ConsoleSpinner : IDisposable
{       
    public ConsoleSpinner()
    {
        CursorLeft = Console.CursorLeft;
        CursorTop = Console.CursorTop;  
    }

    public ConsoleSpinner(bool start)
        : this()
    {
        if (start) Start();
    }

    public void Start()
    {
        // prevent two conflicting Start() calls ot the same instance
        lock (instanceLocker) 
        {
            if (!running )
            {
                running = true;
                turner = new Thread(Turn);
                turner.Start();
            }
        }
    }

    public void StartHere()
    {
        SetPosition();
        Start();
    }

    public void Stop()
    {
        lock (instanceLocker)
        {
            if (!running) return;

            running = false;
            if (! turner.Join(250))
                turner.Abort();
        }
    }

    public void SetPosition()
    {
        SetPosition(Console.CursorLeft, Console.CursorTop);
    }

    public void SetPosition(int left, int top)
    {
        bool wasRunning;
        //prevent other start/stops during move
        lock (instanceLocker)
        {
            wasRunning = running;
            Stop();

            CursorLeft = left;
            CursorTop = top;

            if (wasRunning) Start();
        } 
    }

    public bool IsSpinning { get { return running;} }

    /* ---  PRIVATE --- */

    private int counter=-1;
    private Thread turner; 
    private bool running = false;
    private int rate = 100;
    private int CursorLeft;
    private int CursorTop;
    private Object instanceLocker = new Object();
    private static Object console = new Object();

    private void Turn()
    {
        while (running)
        {
            counter++;

            // prevent two instances from overlapping cursor position updates
            // weird things can still happen if the main ui thread moves the cursor during an update and context switch
            lock (console)
            {                  
                int OldLeft = Console.CursorLeft;
                int OldTop = Console.CursorTop;
                Console.SetCursorPosition(CursorLeft, CursorTop);

                switch (counter)
                {
                    case 0: Console.Write("/"); break;
                    case 1: Console.Write("-"); break;
                    case 2: Console.Write("\\"); break;
                    case 3: Console.Write("|"); counter = -1; break;
                }
                Console.SetCursorPosition(OldLeft, OldTop);
            }

            Thread.Sleep(rate);
        }
        lock (console)
        {   // clean up
            int OldLeft = Console.CursorLeft;
            int OldTop = Console.CursorTop;
            Console.SetCursorPosition(CursorLeft, CursorTop);
            Console.Write(' ');
            Console.SetCursorPosition(OldLeft, OldTop);
        }
    }

    public void Dispose()
    {
        Stop();
    }
}
Joel Coehoorn
la source
Belle modification, bien que l'exemple de code ne soit pas le mien. Il est tiré du blog de Brad Abrams (voir le lien dans ma réponse). Je pense que cela vient d'être écrit comme un simple échantillon démontrant SetCursorPosition. Btw, je suis vraiment surpris (d'une manière positive) de la discussion commencée sur ce que je pensais être juste un simple échantillon. C'est pourquoi j'aime ce site :-)
Dirk Vollmar
4

Utiliser explicitement un retour chariot (\ r) au début de la ligne plutôt que (implicitement ou explicitement) utiliser une nouvelle ligne (\ n) à la fin devrait obtenir ce que vous voulez. Par exemple:

void demoPercentDone() {
    for(int i = 0; i < 100; i++) {
        System.Console.Write( "\rProcessing {0}%...", i );
        System.Threading.Thread.Sleep( 1000 );
    }
    System.Console.WriteLine();    
}
James Hugard
la source
-1, la question demande C #, je le réécris en C # et vous le remettez en F #
Malfist
Cela ressemble à un conflit d'édition plutôt que de lui faire revenir votre C # en F #. Son changement a eu lieu une minute après le vôtre et s'est concentré sur le sprintf.
Andy
Merci pour l'édition. J'ai tendance à utiliser le mode interactif F # pour tester des choses et j'ai pensé que les parties importantes étaient les appels BCL, qui sont les mêmes en C #.
James Hugard
3
    public void Update(string data)
    {
        Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
        Console.Write(string.Format("\r{0}", data));
    }
Jose
la source
1

À partir des documents de la console dans MSDN:

Vous pouvez résoudre ce problème en définissant la propriété TextWriter.NewLine de la propriété Out ou Error sur une autre chaîne de terminaison de ligne. Par exemple, l'instruction C #, Console.Error.NewLine = "\ r \ n \ r \ n" ;, définit la chaîne de terminaison de ligne pour le flux de sortie d'erreur standard sur deux séquences de retour chariot et de saut de ligne. Ensuite, vous pouvez explicitement appeler la méthode WriteLine de l'objet de flux de sortie d'erreur, comme dans l'instruction C #, Console.Error.WriteLine ();

Alors - je l'ai fait:

Console.Out.Newline = String.Empty;

Ensuite, je peux contrôler moi-même la sortie;

Console.WriteLine("Starting item 1:");
    Item1();
Console.WriteLine("OK.\nStarting Item2:");

Une autre façon d'y arriver.

Je veux Bea Programmer.
la source
Vous pouvez simplement utiliser Console.Write () dans le même but, sans redéfinir la propriété NewLine ...
Radosław Gers
1

Cela fonctionne si vous voulez rendre la génération de fichiers cool.

                int num = 1;
                var spin = new ConsoleSpinner();
                Console.ForegroundColor = ConsoleColor.Green;
                Console.Write("");
                while (true)
                {
                    spin.Turn();
                    Console.Write("\r{0} Generating Files ", num);
                    num++;
                }

Et c'est la méthode que j'ai obtenue d'une réponse ci-dessous et l'ai modifiée

public class ConsoleSpinner
    {
        int counter;

        public void Turn()
        {
            counter++;
            switch (counter % 4)
            {
                case 0: Console.Write("."); counter = 0; break;
                case 1: Console.Write(".."); break;
                case 2: Console.Write("..."); break;
                case 3: Console.Write("...."); break;
                case 4: Console.Write("\r"); break;
            }
            Thread.Sleep(100);
            Console.SetCursorPosition(23, Console.CursorTop);
        }
    }
E.Lahu
la source
0

En voici un autre: D

class Program
{
    static void Main(string[] args)
    {
        Console.Write("Working... ");
        int spinIndex = 0;
        while (true)
        {
            // obfuscate FTW! Let's hope overflow is disabled or testers are impatient
            Console.Write("\b" + @"/-\|"[(spinIndex++) & 3]);
        }
    }
}
À M
la source
0

Si vous souhaitez mettre à jour une ligne, mais que les informations sont trop longues pour s'afficher sur une seule ligne, il peut avoir besoin de nouvelles lignes. J'ai rencontré ce problème et voici une façon de résoudre ce problème.

public class DumpOutPutInforInSameLine
{

    //content show in how many lines
    int TotalLine = 0;

    //start cursor line
    int cursorTop = 0;

    // use to set  character number show in one line
    int OneLineCharNum = 75;

    public void DumpInformation(string content)
    {
        OutPutInSameLine(content);
        SetBackSpace();

    }
    static void backspace(int n)
    {
        for (var i = 0; i < n; ++i)
            Console.Write("\b \b");
    }

    public  void SetBackSpace()
    {

        if (TotalLine == 0)
        {
            backspace(OneLineCharNum);
        }
        else
        {
            TotalLine--;
            while (TotalLine >= 0)
            {
                backspace(OneLineCharNum);
                TotalLine--;
                if (TotalLine >= 0)
                {
                    Console.SetCursorPosition(OneLineCharNum, cursorTop + TotalLine);
                }
            }
        }

    }

    private void OutPutInSameLine(string content)
    {
        //Console.WriteLine(TotalNum);

        cursorTop = Console.CursorTop;

        TotalLine = content.Length / OneLineCharNum;

        if (content.Length % OneLineCharNum > 0)
        {
            TotalLine++;

        }

        if (TotalLine == 0)
        {
            Console.Write("{0}", content);

            return;

        }

        int i = 0;
        while (i < TotalLine)
        {
            int cNum = i * OneLineCharNum;
            if (i < TotalLine - 1)
            {
                Console.WriteLine("{0}", content.Substring(cNum, OneLineCharNum));
            }
            else
            {
                Console.Write("{0}", content.Substring(cNum, content.Length - cNum));
            }
            i++;

        }
    }

}
class Program
{
    static void Main(string[] args)
    {

        DumpOutPutInforInSameLine outPutInSameLine = new DumpOutPutInforInSameLine();

        outPutInSameLine.DumpInformation("");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");


        outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");

        //need several lines
        outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");

        outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbb");

    }
}
lisunde
la source
0

je cherchais la même solution sur vb.net et j'ai trouvé celle-ci et elle est géniale.

cependant, comme @JohnOdom a suggéré une meilleure façon de gérer l'espace des blancs si le précédent est plus grand que celui actuel.

je fais une fonction dans vb.net et pensais que quelqu'un pourrait être aidé ..

voici mon code:

Private Sub sPrintStatus(strTextToPrint As String, Optional boolIsNewLine As Boolean = False)
    REM intLastLength is declared as public variable on global scope like below
    REM intLastLength As Integer
    If boolIsNewLine = True Then
        intLastLength = 0
    End If
    If intLastLength > strTextToPrint.Length Then
        Console.Write(Convert.ToChar(13) & strTextToPrint.PadRight(strTextToPrint.Length + (intLastLength - strTextToPrint.Length), Convert.ToChar(" ")))
    Else
        Console.Write(Convert.ToChar(13) & strTextToPrint)
    End If
    intLastLength = strTextToPrint.Length
End Sub
Zakir_SZH
la source
Ici , vous pouvez utiliser la fonction VB d'une variable statique locale: Static intLastLength As Integer.
Mark Hurd
0

Je faisais une recherche pour voir si la solution que j'avais écrite pouvait être optimisée pour la vitesse. Ce que je voulais, c'était un compte à rebours, pas seulement la mise à jour de la ligne actuelle. Voici ce que j'ai trouvé. Pourrait être utile à quelqu'un

            int sleepTime = 5 * 60;    // 5 minutes

            for (int secondsRemaining = sleepTime; secondsRemaining > 0; secondsRemaining --)
            {
                double minutesPrecise = secondsRemaining / 60;
                double minutesRounded = Math.Round(minutesPrecise, 0);
                int seconds = Convert.ToInt32((minutesRounded * 60) - secondsRemaining);
                Console.Write($"\rProcess will resume in {minutesRounded}:{String.Format("{0:D2}", -seconds)} ");
                Thread.Sleep(1000);
            }
            Console.WriteLine("");
Adam Hey
la source
0

Inspiré par @ E.Lahu Solution, mise en place d'une barre de progression avec pourcentage.

public class ConsoleSpinner
{
    private int _counter;

    public void Turn(Color color, int max, string prefix = "Completed", string symbol = "■",int position = 0)
    {
        Console.SetCursorPosition(0, position);
        Console.Write($"{prefix} {ComputeSpinner(_counter, max, symbol)}", color);
        _counter = _counter == max ? 0 : _counter + 1;
    }

    public string ComputeSpinner(int nmb, int max, string symbol)
    {
        var spinner = new StringBuilder();
        if (nmb == 0)
            return "\r ";

        spinner.Append($"[{nmb}%] [");
        for (var i = 0; i < max; i++)
        {
            spinner.Append(i < nmb ? symbol : ".");
        }

        spinner.Append("]");
        return spinner.ToString();
    }
}


public static void Main(string[] args)
    {
        var progressBar= new ConsoleSpinner();
        for (int i = 0; i < 1000; i++)
        {
            progressBar.Turn(Color.Aqua,100);
            Thread.Sleep(1000);
        }
    }
valériane Havaux
la source
0

Voici mon point de vue sur les réponses de soosh et 0xA3. Il peut mettre à jour la console avec des messages utilisateur lors de la mise à jour du spinner et dispose également d'un indicateur de temps écoulé.

public class ConsoleSpiner : IDisposable
{
    private static readonly string INDICATOR = "/-\\|";
    private static readonly string MASK = "\r{0} {1:c} {2}";
    int counter;
    Timer timer;
    string message;

    public ConsoleSpiner() {
        counter = 0;
        timer = new Timer(200);
        timer.Elapsed += TimerTick;
    }

    public void Start() {
        timer.Start();
    }

    public void Stop() {
        timer.Stop();
        counter = 0;
    }

    public string Message {
        get { return message; }
        set { message = value; }
    }

    private void TimerTick(object sender, ElapsedEventArgs e) {
        Turn();
    }

    private void Turn() {
        counter++;
        var elapsed = TimeSpan.FromMilliseconds(counter * 200);
        Console.Write(MASK, INDICATOR[counter % 4], elapsed, this.Message);
    }

    public void Dispose() {
        Stop();
        timer.Elapsed -= TimerTick;
        this.timer.Dispose();
    }
}

l'utilisation est quelque chose comme ceci:

class Program
{
    static void Main(string[] args)
    {
        using (var spinner = new ConsoleSpiner())
        {
            spinner.Start();
            spinner.Message = "About to do some heavy staff :-)"
            DoWork();
            spinner.Message = "Now processing other staff".
            OtherWork();
            spinner.Stop();
        }
        Console.WriteLine("COMPLETED!!!!!\nPress any key to exit.");

    }
}
cleftheris
la source
-1

La SetCursorPositionméthode fonctionne dans un scénario multi-thread, où les deux autres méthodes ne le font pas

imgen
la source