¿Cómo puedo actualizar la línea actual en una aplicación de consola de Windows C #?

507

Al crear una aplicación de consola de Windows en C #, ¿es posible escribir en la consola sin tener que extender una línea actual o ir a una nueva? Por ejemplo, si quiero mostrar un porcentaje que represente qué tan cerca está un proceso de completarse, me gustaría actualizar el valor en la misma línea que el cursor y no tener que poner cada porcentaje en una nueva línea.

¿Se puede hacer esto con una aplicación de consola C # "estándar"?

Vengador IVR
fuente
Si está REALMENTE interesado en interfaces de línea de comandos interesantes, debería revisar curses / ncurses.
Charles Addis
@CharlesAddis, pero ¿curses / ncurses no funciona solo en C ++?
Xam

Respuestas:

783

Si imprime solo "\r"en la consola, el cursor vuelve al comienzo de la línea actual y luego puede reescribirlo. Esto debería funcionar:

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

Observe los pocos espacios después del número para asegurarse de que se borre todo lo que había antes.
Observe también el uso de en Write()lugar de WriteLine()ya que no desea agregar un "\ n" al final de la línea.

shoosh
fuente
77
for (int i = 0; i <= 100; ++ i) irá al 100%
Nicolas Tyler
13
¿Cómo se maneja cuando la escritura anterior fue más larga que la nueva escritura? ¿Hay alguna forma de obtener el ancho de la consola y rellenar la línea con espacios, tal vez?
Drew Chapin el
66
@druciferre Fuera de mi cabeza se me ocurren dos respuestas para su pregunta. Ambos implican guardar primero la salida actual como una cadena y rellenarla con una cantidad establecida de caracteres como este: Console.Write ("\ r {0}", strOutput.PadRight (nPaddingCount, '')); El "nPaddingCount" puede ser un número que establezca usted mismo o puede realizar un seguimiento de la salida anterior y establecer nPaddingCount como la diferencia de longitud entre la salida anterior y actual más la longitud de salida actual. Si nPaddingCount es negativo, entonces no tendría que usar PadRight a menos que haga abdominales (prev.len - curr.len).
John Odom el
1
@malgm Código bien organizado. Si cualquiera de una docena de hilos pudiera escribir en la consola en cualquier momento que quisiera, eso le dará problemas sin importar si está escribiendo nuevas líneas o no.
Mark
2
@JohnOdom solo necesita mantener la longitud de salida anterior (sin relleno) y luego introducirla como el primer argumento para PadRight(guardar la cadena sin relleno, o longitud, primero, por supuesto).
Jesper Matthiesen
254

Puede usar Console.SetCursorPositionpara establecer la posición del cursor y luego escribir en la posición actual.

Aquí hay un ejemplo que muestra 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);
    }
}

Tenga en cuenta que deberá asegurarse de sobrescribir cualquier salida existente con nueva salida o espacios en blanco.

Actualización: como se ha criticado que el ejemplo mueve el cursor solo un carácter hacia atrás, agregaré esto para aclararlo: al usarlo SetCursorPosition, puede colocar el cursor en cualquier posición en la ventana de la consola.

Console.SetCursorPosition(0, Console.CursorTop);

configurará el cursor al comienzo de la línea actual (o puede usarlo Console.CursorLeft = 0directamente).

Dirk Vollmar
fuente
8
El problema puede resolverse usando \ r, pero usar SetCursorPosition(o CursorLeft) permite una mayor flexibilidad, por ejemplo, no escribir al comienzo de la línea, subir en la ventana, etc., por lo que es un enfoque más general que puede usarse para, por ejemplo, la salida barras de progreso personalizadas o gráficos ASCII.
Dirk Vollmar
14
+1 por ser detallado e ir más allá del llamado del deber. Buenas cosas gracias.
Copas
1
+1 por mostrar una forma diferente de hacerlo. Todos los demás mostraron \ r, y si el OP simplemente está actualizando un porcentaje, con esto simplemente puede actualizar el valor sin tener que volver a escribir toda la línea. El OP nunca dijo realmente que quería moverse al comienzo de la línea, solo que quería actualizar algo en la misma línea que el cursor.
Andy
1
La flexibilidad adicional de SetCursorPosition tiene el costo de un poco de velocidad y un parpadeo notable del cursor si el bucle es lo suficientemente largo para que el usuario lo note. Vea mi comentario de prueba a continuación.
Kevin
55
También confirme que la longitud de la línea no hace que la consola se ajuste a la siguiente línea o de todos modos puede tener problemas con el contenido que se ejecuta en la ventana de la consola.
Mandrake
84

Hasta ahora tenemos tres alternativas competitivas sobre cómo hacer esto:

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
}

Siempre he usado Console.CursorLeft = 0una variación de la tercera opción, así que decidí hacer algunas pruebas. Aquí está el código que usé:

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

En mi máquina, obtengo los siguientes resultados:

  • Espacios traseros: 25.0 segundos
  • Devoluciones de carro: 28.7 segundos
  • SetCursorPosition: 49.7 segundos

Además, SetCursorPositioncausó un notable parpadeo que no observé con ninguna de las alternativas. Por lo tanto, la moraleja es usar espacios de retroceso o retornos de carro cuando sea posible , y gracias por enseñarme una forma más rápida de hacer esto, ¡SO!


Actualización : en los comentarios, Joel sugiere que SetCursorPosition es constante con respecto a la distancia movida mientras que los otros métodos son lineales. Pruebas adicionales confirman que este es el caso, sin embargo , el tiempo constante y lento aún es lento. En mis pruebas, escribir una larga cadena de espacios de retroceso en la consola es más rápido que SetCursorPosition hasta aproximadamente 60 caracteres. Entonces, el retroceso es más rápido para reemplazar porciones de la línea de menos de 60 caracteres (más o menos), y no parpadea, por lo que voy a mantener mi respaldo inicial de \ b sobre \ r y SetCursorPosition.

Kevin
fuente
44
La eficiencia de la operación en cuestión realmente no debería importar. Todo debería ocurrir demasiado rápido para que el usuario lo note. La microptimización innecesaria es mala.
Malfist
@Malfist: Dependiendo de la longitud del bucle, el usuario puede o no darse cuenta. Como agregué en la edición anterior (antes de ver su comentario), SetCursorPosition introdujo el parpadeo y toma casi el doble de tiempo que las otras opciones.
Kevin
1
Estoy de acuerdo en que es una microoptimización (ejecutarlo un millón de veces y tomar 50 segundos sigue siendo una cantidad muy pequeña de tiempo), +1 para los resultados, y definitivamente podría ser muy útil saberlo.
Andy
66
El punto de referencia es fundamentalmente defectuoso. Es posible que el tiempo de SetCursorPosition () sea el mismo sin importar qué tan lejos se mueva el cursor, mientras que las otras opciones varían según la cantidad de caracteres que la consola tiene que procesar.
Joel Coehoorn
1
Esta es una muy buena suma de las diferentes opciones disponibles. Sin embargo, también veo parpadeo cuando uso \ r. Con \ b obviamente no hay parpadeo porque el texto de corrección ("Conteo:") no se reescribe. También obtendrá un parpadeo si agrega \ b adicional y reescribe el texto de reparación como está sucediendo con \ b y SetCursorPosition. Con respecto al comentario de Joel: Joel tiene razón básicamente, sin embargo, \ r aún superará a SetCursorPosition en líneas muy largas, pero la diferencia es menor.
Dirk Vollmar
27

Puede usar la secuencia de escape \ b (retroceso) para hacer una copia de seguridad de un número particular de caracteres en la línea actual. Esto solo mueve la ubicación actual, no elimina los caracteres.

Por ejemplo:

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

Aquí, línea es el porcentaje de línea para escribir en la consola. El truco consiste en generar el número correcto de caracteres \ b para la salida anterior.

La ventaja de esto sobre el enfoque \ r es que funciona incluso si su porcentaje de salida no está al comienzo de la línea.

Sean
fuente
1
+1, este resulta ser el método más rápido presentado (ver mi comentario de prueba a continuación)
Kevin
19

\rse usa para estos escenarios.
\r representa un retorno de carro, lo que significa que el cursor vuelve al inicio de la línea.
Es por eso que Windows usa \n\rcomo su nuevo marcador de línea.
\nte mueve hacia abajo una línea y \rte regresa al comienzo de la línea.

Malfist
fuente
22
Excepto que en realidad es \ r \ n.
Joel Mueller
14

Solo tenía que jugar con la ConsoleSpinnerclase del divo . El mío no es tan conciso, pero no me pareció bien que los usuarios de esa clase tengan que escribir su propio while(true)bucle. Estoy filmando para una experiencia más como esta:

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

    // Do some work...

    spin.Stop(); 
}

Y me di cuenta con el siguiente código. Como no quiero que mi Start()método se bloquee, no quiero que el usuario tenga que preocuparse por escribir un while(spinFlag)bucle similar, y quiero permitir que varias hiladoras al mismo tiempo tengan que generar un hilo separado para manejar el hilado. Y eso significa que el código tiene que ser mucho más complicado.

Además, no he hecho tantos subprocesos múltiples, por lo que es posible (incluso probable) que haya dejado un error sutil o tres allí. Pero parece funcionar bastante bien hasta ahora:

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
fuente
Buena modificación, aunque el código de muestra no es mío. Está tomado del blog de Brad Abrams (ver el enlace en mi respuesta). Creo que solo se ha escrito como una muestra simple que demuestra SetCursorPosition. Por cierto, definitivamente estoy sorprendido (de manera positiva) sobre la discusión que comenzó sobre lo que pensé que era solo una muestra simple. Es por eso que amo este sitio :-)
Dirk Vollmar
4

El uso explícito de un Retorno de Carrage (\ r) al comienzo de la línea en lugar de (implícita o explícitamente) el uso de una Nueva Línea (\ n) al final debería obtener lo que desea. Por ejemplo:

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
fuente
-1, Pregunta pide C #, lo reescribo en C # y lo cambias a F #
Malfist
Parece un conflicto de edición en lugar de que él cambie su C # de nuevo a F #. Su cambio fue un minuto después del tuyo y se centró en el sprintf.
Andy
Gracias por la edición Tiendo a usar el modo interactivo F # para probar cosas y pensé que las partes importantes eran las llamadas BCL, que son las mismas 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
fuente
1

Desde los documentos de la consola en MSDN:

Puede resolver este problema estableciendo la propiedad TextWriter.NewLine de la propiedad Out o Error en otra cadena de terminación de línea. Por ejemplo, la instrucción C #, Console.Error.NewLine = "\ r \ n \ r \ n" ;, establece la cadena de terminación de línea para la secuencia de salida de error estándar en dos secuencias de retorno de carro y avance de línea. Luego puede llamar explícitamente al método WriteLine del objeto de flujo de salida de error, como en la instrucción C #, Console.Error.WriteLine ();

Entonces, hice esto:

Console.Out.Newline = String.Empty;

Entonces puedo controlar la salida yo mismo;

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

Otra forma de llegar allí.

Quiero ser Bea Programmer.
fuente
Podría usar Console.Write () para el mismo propósito, sin redefinir la propiedad NewLine ...
Radosław Gers
1

Esto funciona si desea que la generación de archivos se vea genial.

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

Y este es el método que obtuve de alguna respuesta a continuación y la modifiqué

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
fuente
0

Aquí hay otro: 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]);
        }
    }
}
Tom
fuente
0

Si desea actualizar una línea, pero la información es demasiado larga para mostrarse en una línea, es posible que necesite algunas líneas nuevas. Me he encontrado con este problema, y ​​a continuación hay una forma de resolverlo.

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
fuente
0

Estaba buscando la misma solución en vb.net y encontré esta y es genial.

sin embargo, como @JohnOdom sugirió una mejor manera de manejar el espacio en blanco si el anterior es más grande que el actual.

hice una función en vb.net y pensé que alguien podría recibir ayuda ...

Aquí está mi código:

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
fuente
Aquí se puede utilizar la función de VB de una variable estática local: Static intLastLength As Integer.
Mark Hurd
0

Estaba haciendo una búsqueda de esto para ver si la solución que escribí podría optimizarse para la velocidad. Lo que quería era un temporizador de cuenta regresiva, no solo actualizar la línea actual. Esto es lo que se me ocurrió. Puede ser útil para alguien

            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
fuente
0

Inspirado por @ E.Lahu Solution, implementación de una barra de progreso con porcentaje.

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);
        }
    }
valeriana Havaux
fuente
0

Aquí está mi opinión sobre las respuestas de s soosh y 0xA3. Puede actualizar la consola con mensajes de usuario mientras actualiza la ruleta y también tiene un indicador de tiempo transcurrido.

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

el uso es algo como esto:

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
fuente
-1

El SetCursorPositionmétodo funciona en un escenario de subprocesos múltiples, donde los otros dos métodos no

imgen
fuente