Wie kann ich die aktuelle Zeile in einer C # Windows Console App aktualisieren?


507

Ist es beim Erstellen einer Windows-Konsolen-App in C # möglich, in die Konsole zu schreiben, ohne eine aktuelle Zeile erweitern oder in eine neue Zeile wechseln zu müssen? Wenn ich beispielsweise einen Prozentsatz anzeigen möchte, der angibt, wie kurz ein Prozess vor dem Abschluss steht, möchte ich nur den Wert in derselben Zeile wie den Cursor aktualisieren und muss nicht jeden Prozentsatz in eine neue Zeile setzen.

Kann dies mit einer "Standard" C # -Konsolen-App durchgeführt werden?


Wenn Sie WIRKLICH an coolen Befehlszeilenschnittstellen interessiert sind, sollten Sie curses / ncurses ausprobieren.
Charles Addis

@CharlesAddis, aber funktioniert curses / ncurses nicht nur in C ++?
Xam

Antworten:


783

Wenn Sie nur "\r"auf der Konsole drucken , kehrt der Cursor zum Anfang der aktuellen Zeile zurück und Sie können ihn neu schreiben. Dies sollte den Trick tun:

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

Beachten Sie die wenigen Leerzeichen nach der Nummer, um sicherzustellen, dass alles, was zuvor vorhanden war, gelöscht wird.
Beachten Sie auch die Verwendung von Write()anstelle von, WriteLine()da Sie am Ende der Zeile kein "\ n" hinzufügen möchten.


7
denn (int i = 0; i <= 100; ++ i) wird zu 100% gehen
Nicolas Tyler

13
Wie gehen Sie vor, wenn der vorherige Schreibvorgang länger war als der neue? Gibt es eine Möglichkeit, die Breite der Konsole zu ermitteln und die Linie mit Leerzeichen zu füllen?
Drew Chapin

6
@druciferre Auf den ersten Blick fallen mir zwei Antworten auf Ihre Frage ein. Bei beiden wird die aktuelle Ausgabe zuerst als Zeichenfolge gespeichert und mit einer festgelegten Anzahl von Zeichen wie folgt aufgefüllt: Console.Write ("\ r {0}", strOutput.PadRight (nPaddingCount, '')); Der "nPaddingCount" kann eine Zahl sein, die Sie selbst festgelegt haben, oder Sie können die vorherige Ausgabe verfolgen und nPaddingCount als Längenunterschied zwischen der vorherigen und der aktuellen Ausgabe plus der aktuellen Ausgabelänge festlegen. Wenn nPaddingCount negativ ist, müssen Sie PadRight nur verwenden, wenn Sie abs (prev.len - curr.len) ausführen.
John Odom

1
@malgm Gut organisierter Code. Wenn eines von einem Dutzend Threads jederzeit auf die Konsole schreiben kann, gibt es Probleme, unabhängig davon, ob Sie neue Zeilen schreiben oder nicht.
Mark

2
@JohnOdom Sie müssen nur die vorherige (ungepolsterte) Ausgabelänge beibehalten und diese dann als erstes Argument eingeben ( PadRightnatürlich zuerst die ungepolsterte Zeichenfolge oder Länge speichern).
Jesper Matthiesen

254

Mit können Sie Console.SetCursorPositiondie Position des Cursors einstellen und dann an der aktuellen Position schreiben.

Hier ist ein Beispiel, das einen einfachen "Spinner" zeigt:

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

Beachten Sie, dass Sie sicherstellen müssen, dass vorhandene Ausgaben mit neuen Ausgaben oder Leerzeichen überschrieben werden.

Update: Da kritisiert wurde, dass das Beispiel den Cursor nur um ein Zeichen zurückbewegt, füge ich dies zur Verdeutlichung hinzu: Mit können SetCursorPositionSie den Cursor auf eine beliebige Position im Konsolenfenster setzen.

Console.SetCursorPosition(0, Console.CursorTop);

setzt den Cursor auf den Anfang der aktuellen Zeile (oder Sie können ihn Console.CursorLeft = 0direkt verwenden).


8
Das Problem kann mit \ r gelöst werden, aber mit SetCursorPosition(oder CursorLeft) können Sie flexibler arbeiten, z. B. nicht am Zeilenanfang schreiben, im Fenster nach oben gehen usw. Dies ist also ein allgemeinerer Ansatz, der z. B. für die Ausgabe verwendet werden kann Benutzerdefinierte Fortschrittsbalken oder ASCII-Grafik.
Dirk Vollmar

14
+1 für wortreich sein und über die Pflicht hinausgehen. Gutes Zeug, danke.
Copas

1
+1 für eine andere Vorgehensweise. Alle anderen haben \ r angezeigt, und wenn das OP lediglich einen Prozentsatz aktualisiert, kann er damit nur den Wert aktualisieren, ohne die gesamte Zeile neu schreiben zu müssen. Das OP hat nie gesagt, dass er an den Anfang der Zeile gehen möchte, nur dass er etwas in derselben Zeile wie den Cursor aktualisieren möchte.
Andy

1
Die zusätzliche Flexibilität von SetCursorPosition geht zu Lasten der Geschwindigkeit und eines spürbaren Cursorflackerns, wenn die Schleife lang genug ist, damit der Benutzer sie bemerkt. Siehe meinen Testkommentar unten.
Kevin

5
Stellen Sie außerdem sicher, dass die Konsolenlänge nicht dazu führt, dass die Konsole in die nächste Zeile übergeht, da sonst möglicherweise Probleme mit dem Inhalt auftreten, der im Konsolenfenster ausgeführt wird.
Mandrake

84

Bisher haben wir drei konkurrierende Alternativen, um dies zu tun:

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
}

Ich habe immer Console.CursorLeft = 0eine Variation der dritten Option verwendet, also habe ich beschlossen, einige Tests durchzuführen. Hier ist der Code, den ich verwendet habe:

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

Auf meinem Computer erhalte ich folgende Ergebnisse:

  • Backspaces: 25,0 Sekunden
  • Wagenrücklauf: 28,7 Sekunden
  • SetCursorPosition: 49,7 Sekunden

Außerdem SetCursorPositionverursachte sich ein merkliches Flackern, das ich bei keiner der Alternativen beobachtete. Die Moral ist es also, wenn möglich Backspaces oder Wagenrückläufe zu verwenden , und danke, dass Sie mir einen schnelleren Weg beigebracht haben, SO!


Update : In den Kommentaren schlägt Joel vor, dass SetCursorPosition in Bezug auf die zurückgelegte Strecke konstant ist, während die anderen Methoden linear sind. Weitere Tests bestätigen, dass dies der Fall ist, jedoch ist konstante Zeit und langsam immer noch langsam. In meinen Tests ist das Schreiben einer langen Reihe von Backspaces in die Konsole schneller als bei SetCursorPosition, bis etwa 60 Zeichen erreicht sind. Die Rücktaste ist also schneller, um Teile der Zeile zu ersetzen, die kürzer als 60 Zeichen (oder so) sind, und sie flackert nicht. Daher werde ich zu meiner anfänglichen Bestätigung von \ b über \ r und stehen SetCursorPosition.


4
Die Effizienz des betreffenden Vorgangs sollte eigentlich keine Rolle spielen. Es sollte alles zu schnell geschehen, als dass der Benutzer es bemerken könnte. Unnötige Mikroptimierung ist schlecht.
Malfist

@Malfist: Abhängig von der Länge der Schleife kann der Benutzer dies bemerken oder nicht. Wie ich oben in der Bearbeitung hinzugefügt habe (bevor ich Ihren Kommentar gesehen habe), hat SetCursorPosition Flimmern eingeführt und dauert fast doppelt so lange wie die anderen Optionen.
Kevin

1
Ich bin damit einverstanden, dass es sich um eine Mikrooptimierung handelt (millionenfaches Ausführen und 50 Sekunden dauert immer noch sehr wenig), +1 für die Ergebnisse, und es könnte definitiv sehr nützlich sein, dies zu wissen.
Andy

6
Die Benchmark ist grundlegend fehlerhaft. Es ist möglich, dass die SetCursorPosition () -Zeit gleich ist, unabhängig davon, wie weit sich der Cursor bewegt, während die anderen Optionen davon abhängen, wie viele Zeichen die Konsole verarbeiten muss.
Joel Coehoorn

1
Dies ist eine sehr schöne Zusammenfassung der verschiedenen verfügbaren Optionen. Ich sehe jedoch auch ein Flackern bei Verwendung von \ r. Mit \ b gibt es offensichtlich kein Flackern, da der Fixtext ("Counting:") nicht neu geschrieben wird. Sie werden auch flackern, wenn Sie zusätzliches \ b hinzufügen und den Fixtext neu schreiben, wie es mit \ b und SetCursorPosition geschieht. In Bezug auf Joels Bemerkung: Joel hat im Grunde recht, aber \ r wird SetCursorPosition in sehr langen Zeilen immer noch übertreffen, aber der Unterschied wird geringer.
Dirk Vollmar

27

Sie können die Escape-Sequenz \ b (Rücktaste) verwenden, um eine bestimmte Anzahl von Zeichen in der aktuellen Zeile zu sichern. Dadurch wird nur der aktuelle Speicherort verschoben und die Zeichen werden nicht entfernt.

Zum Beispiel:

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

Hier ist Zeile die prozentuale Zeile, die in die Konsole geschrieben werden soll. Der Trick besteht darin, die richtige Anzahl von \ b Zeichen für die vorherige Ausgabe zu generieren .

Der Vorteil gegenüber dem \ r- Ansatz besteht darin, dass if funktioniert, auch wenn sich Ihre prozentuale Ausgabe nicht am Zeilenanfang befindet.


1
+1, dies stellt sich als die schnellste vorgestellte Methode heraus (siehe meinen Testkommentar unten)
Kevin

19

\rwird für diese Szenarien verwendet.
\r stellt einen Wagenrücklauf dar, was bedeutet, dass der Cursor zum Zeilenanfang zurückkehrt.
Aus diesem Grund verwendet Windows \n\rals neue Linienmarkierung.
\nBewegt Sie eine Zeile nach unten und \rbringt Sie zum Anfang der Zeile zurück.


22
Außer es ist tatsächlich \ r \ n.
Joel Mueller

14

Ich musste nur mit der ConsoleSpinnerKlasse des Divos spielen . Meins ist bei weitem nicht so prägnant, aber es hat mir einfach nicht gut gefallen, dass Benutzer dieser Klasse ihre eigene while(true)Schleife schreiben müssen . Ich fotografiere für eine Erfahrung wie diese:

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

    // Do some work...

    spin.Stop(); 
}

Und ich habe es mit dem folgenden Code realisiert. Da ich nicht möchte, dass meine Start()Methode blockiert, möchte ich nicht, dass sich der Benutzer um das Schreiben einer while(spinFlag)ähnlichen Schleife kümmern muss , und ich möchte mehreren Spinnern gleichzeitig erlauben, dass ich einen separaten Thread erzeugen muss, um das zu handhaben Spinnen. Und das bedeutet, dass der Code viel komplizierter sein muss.

Außerdem habe ich nicht so viel Multithreading durchgeführt, so dass es möglich ist (wahrscheinlich sogar), dass ich ein oder drei subtile Fehler hinterlassen habe. Aber es scheint bisher ziemlich gut zu funktionieren:

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

Schöne Modifikation, obwohl der Beispielcode nicht meiner ist. Es stammt aus Brad Abrams 'Blog (siehe den Link in meiner Antwort). Ich denke, es wurde nur als einfaches Beispiel geschrieben, das SetCursorPosition demonstriert. Übrigens bin ich definitiv (positiv) überrascht, dass die Diskussion darüber begonnen hat, was ich für ein einfaches Beispiel hielt. Deshalb liebe ich diese Seite :-)
Dirk Vollmar

4

Die explizite Verwendung eines Carrage Return (\ r) am Anfang der Zeile anstelle (implizit oder explizit) der Verwendung einer neuen Zeile (\ n) am Ende sollte das gewünschte Ergebnis erzielen. Zum Beispiel:

void demoPercentDone() {
    for(int i = 0; i < 100; i++) {
        System.Console.Write( "\rProcessing {0}%...", i );
        System.Threading.Thread.Sleep( 1000 );
    }
    System.Console.WriteLine();    
}

-1, Frage fragt nach C #, ich schreibe es in C # um und du
änderst

Es sieht eher nach einem Bearbeitungskonflikt aus, als dass er Ihr C # wieder in F # ändert. Sein Wechsel war eine Minute nach deinem und konzentrierte sich auf den Sprint.
Andy

Danke für die Bearbeitung. Ich neige dazu, den interaktiven F # -Modus zu verwenden, um Dinge zu testen, und stellte fest, dass die wichtigen Teile die BCL-Aufrufe waren, die in C # gleich sind.
James Hugard

3
    public void Update(string data)
    {
        Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
        Console.Write(string.Format("\r{0}", data));
    }

1

Aus den Konsolendokumenten in MSDN:

Sie können dieses Problem lösen, indem Sie die Eigenschaft TextWriter.NewLine der Eigenschaft Out oder Error auf eine andere Zeilenabschlusszeichenfolge setzen. Beispielsweise setzt die C # -Anweisung Console.Error.NewLine = "\ r \ n \ r \ n"; die Zeilenabschlusszeichenfolge für den Standardfehlerausgabestream auf zwei Wagenrücklauf- und Zeilenvorschubsequenzen. Anschließend können Sie die WriteLine-Methode des Fehlerausgabestreamobjekts explizit aufrufen, wie in der C # -Anweisung Console.Error.WriteLine ();

Also - ich habe das gemacht:

Console.Out.Newline = String.Empty;

Dann kann ich die Ausgabe selbst steuern;

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

Ein anderer Weg dorthin.


Sie können Console.Write () einfach für denselben Zweck verwenden, ohne die NewLine-Eigenschaft neu zu definieren ...
Radosław Gers

1

Dies funktioniert, wenn Sie das Generieren von Dateien cool aussehen lassen möchten.

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

Und dies ist die Methode, die ich aus einer Antwort unten erhalten und modifiziert habe

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

0

Hier ist noch einer: 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]);
        }
    }
}

0

Wenn Sie eine Zeile aktualisieren möchten, die Informationen jedoch zu lang sind, um in einer Zeile angezeigt zu werden, sind möglicherweise einige neue Zeilen erforderlich. Ich bin auf dieses Problem gestoßen, und im Folgenden finden Sie eine Möglichkeit, dies zu lösen.

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

    }
}

0

Ich habe nach der gleichen Lösung in vb.net gesucht und diese gefunden und sie ist großartig.

Da @JohnOdom jedoch eine bessere Möglichkeit vorgeschlagen hat, mit dem Leerzeichen umzugehen, wenn das vorherige größer als das aktuelle ist.

Ich mache eine Funktion in vb.net und dachte, jemand könnte geholfen werden ..

Hier ist mein 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

Hier können Sie die VB-Funktion einer lokalen statischen Variablen verwenden : Static intLastLength As Integer.
Mark Hurd

0

Ich habe danach gesucht, um zu sehen, ob die von mir geschriebene Lösung auf Geschwindigkeit optimiert werden kann. Was ich wollte, war ein Countdown-Timer, der nicht nur die aktuelle Zeile aktualisiert. Folgendes habe ich mir ausgedacht. Könnte für jemanden nützlich sein

            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("");

0

Inspiriert von @ E.Lahu Solution, Implementierung eines Balkenfortschritts mit Prozentsatz.

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

0

Hier ist meine Sicht auf die Antworten von s soosh und 0xA3. Es kann die Konsole mit Benutzermeldungen aktualisieren, während der Spinner aktualisiert wird, und verfügt auch über eine Anzeige für die verstrichene Zeit.

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

Nutzung ist so etwas wie:

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.");

    }
}

-1

Die SetCursorPositionMethode funktioniert in einem Multithreading-Szenario, in dem die beiden anderen Methoden dies nicht tun

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.