Bagaimana cara saya memperbarui baris saat ini di Aplikasi Konsol C # Windows?


507

Saat membuat Aplikasi Konsol Windows di C #, apakah mungkin untuk menulis ke konsol tanpa harus memperpanjang baris saat ini atau pergi ke baris baru? Misalnya, jika saya ingin menunjukkan persentase yang menunjukkan seberapa dekat suatu proses dengan penyelesaian, saya hanya ingin memperbarui nilai pada baris yang sama dengan kursor, dan tidak harus meletakkan setiap persentase pada baris baru.

Apakah ini dapat dilakukan dengan aplikasi konsol C # "standar"?


Jika Anda BENAR-BENAR tertarik pada antarmuka baris perintah keren, Anda harus memeriksa kutukan / ncurses.
Charles Addis

@CharlesAddis tetapi tidakkah kutukan / ncurses hanya berfungsi di C ++?
Xam

Jawaban:


783

Jika Anda hanya mencetak "\r"ke konsol kursor kembali ke awal baris saat ini dan kemudian Anda dapat menulis ulang. Ini harus melakukan trik:

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

Perhatikan beberapa spasi setelah nomor untuk memastikan bahwa apa pun yang ada di sana dihapus.
Perhatikan juga penggunaan Write()alih - alih WriteLine()karena Anda tidak ingin menambahkan "\ n" di akhir baris.


7
untuk (int i = 0; i <= 100; ++ i) akan masuk ke 100%
Nicolas Tyler

13
Bagaimana Anda menangani ketika penulisan sebelumnya lebih panjang dari penulisan baru? Apakah ada cara untuk mendapatkan lebar konsol dan mengisi garis dengan spasi, mungkin?
Drew Chapin

6
@druciferre Dari atas kepala saya, saya bisa memikirkan dua jawaban untuk pertanyaan Anda. Keduanya melibatkan penyimpanan output saat ini sebagai string terlebih dahulu dan padding dengan sejumlah karakter seperti ini: Console.Write ("\ r {0}", strOutput.PadRight (nPaddingCount, '')); "NPaddingCount" dapat berupa angka yang Anda atur sendiri atau Anda dapat melacak output sebelumnya dan mengatur nPaddingCount sebagai perbedaan panjang antara output sebelumnya dan saat ini ditambah panjang output saat ini. Jika nPaddingCount negatif maka Anda tidak perlu menggunakan PadRight kecuali Anda melakukan abs (prev.len - curr.len).
John Odom

1
@malgm Kode terorganisasi dengan baik. Jika ada selusin utas yang dapat menulis ke konsol kapan saja diinginkan, itu akan memberi Anda masalah terlepas dari apakah Anda sedang menulis baris baru atau tidak.
Markus

2
@JohnOdom Anda hanya perlu mempertahankan panjang keluaran sebelumnya (tidak ditambahkan), dan kemudian memasukkannya sebagai argumen pertama untuk PadRight(menyimpan string yang tidak ditambahkan, atau panjang, pertama, tentu saja).
Jesper Matthiesen

254

Anda dapat menggunakan Console.SetCursorPositionuntuk mengatur posisi kursor dan kemudian menulis di posisi saat ini.

Berikut adalah contoh yang menunjukkan "pemintal" sederhana:

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

Perhatikan bahwa Anda harus memastikan untuk menimpa output yang ada dengan output baru atau kosong.

Pembaruan: Karena telah dikritik bahwa contoh hanya memindahkan kursor kembali oleh satu karakter, saya akan menambahkan ini untuk klarifikasi: Menggunakan SetCursorPositionAnda dapat mengatur kursor ke posisi apa pun di jendela konsol.

Console.SetCursorPosition(0, Console.CursorTop);

akan mengatur kursor ke awal baris saat ini (atau Anda dapat menggunakan Console.CursorLeft = 0langsung).


8
Masalahnya mungkin diselesaikan menggunakan \ r, tetapi menggunakan SetCursorPosition(atau CursorLeft) memungkinkan untuk lebih banyak fleksibilitas, misalnya tidak menulis di awal baris, bergerak ke atas di jendela, dll jadi itu adalah pendekatan yang lebih umum yang dapat digunakan untuk misalnya output bilah progres kustom atau grafik ASCII.
Dirk Vollmar

14
+1 untuk menjadi verbose dan melampaui panggilan tugas. Barang bagus terima kasih.
Copas

1
+1 untuk menunjukkan cara melakukannya yang berbeda. Semua orang menunjukkan, dan jika OP hanya memperbarui persentase, dengan ini dia hanya dapat memperbarui nilai tanpa harus menulis ulang seluruh baris. OP tidak pernah benar-benar mengatakan dia ingin pindah ke awal baris, hanya saja dia ingin memperbarui sesuatu pada baris yang sama dengan kursor.
Andy

1
Fleksibilitas tambahan dari SetCursorPosition datang dengan mengorbankan sedikit kecepatan dan kedipan kursor yang terlihat jika loop cukup panjang bagi pengguna untuk memperhatikan. Lihat komentar pengujian saya di bawah ini.
Kevin

5
Juga konfirmasi bahwa panjang garis tidak menyebabkan konsol untuk membungkus ke baris berikutnya atau Anda mungkin mendapatkan masalah dengan konten yang mengalir di jendela konsol.
Mandrake

84

Sejauh ini kami memiliki tiga alternatif yang bersaing untuk bagaimana melakukan ini:

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
}

Saya selalu menggunakan Console.CursorLeft = 0, variasi pada opsi ketiga, jadi saya memutuskan untuk melakukan beberapa tes. Berikut kode yang saya gunakan:

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

Di mesin saya, saya mendapatkan hasil berikut:

  • Spasi mundur: 25,0 detik
  • Pengembalian Carriage: 28,7 detik
  • SetCursorPosition: 49,7 detik

Selain itu, SetCursorPositionmenyebabkan kedipan terlihat yang tidak saya amati dengan salah satu alternatif. Jadi, moralnya adalah menggunakan backspaces atau carriage return jika memungkinkan , dan terima kasih telah mengajari saya cara yang lebih cepat untuk melakukan ini, SO!


Pembaruan : Dalam komentar, Joel menyarankan bahwa SetCursorPosition konstan terhadap jarak yang dipindahkan sementara metode lainnya linier. Pengujian lebih lanjut menegaskan bahwa ini adalah kasusnya, namun waktu yang konstan dan lambat masih lambat. Dalam pengujian saya, menulis string panjang spasi mundur ke konsol lebih cepat daripada SetCursorPosition hingga sekitar 60 karakter. Jadi backspace lebih cepat untuk mengganti bagian dari garis yang lebih pendek dari 60 karakter (atau lebih), dan itu tidak berkedip, jadi saya akan mendukung pengesahan awal saya dari \ b over \ r dan SetCursorPosition.


4
Efisiensi operasi yang dipermasalahkan seharusnya tidak menjadi masalah. Itu semua harus terjadi terlalu cepat bagi pengguna untuk memperhatikan. Microptimisation yang tidak perlu itu buruk.
Malfist

@Malfist: Bergantung pada panjang loop, pengguna mungkin atau mungkin tidak memperhatikan. Seperti yang saya tambahkan di edit di atas (sebelum saya melihat komentar Anda), SetCursorPosition memperkenalkan flicker dan memakan waktu hampir dua kali lebih lama dari opsi lainnya.
Kevin

1
Saya setuju bahwa ini adalah mikro-optimasi (menjalankannya jutaan kali dan mengambil 50 detik masih dalam jumlah yang sangat kecil), +1 untuk hasilnya, dan pasti bisa sangat berguna untuk diketahui.
Andy

6
Benchmark secara fundamental cacat. Mungkin saja waktu SetCursorPosition () sama, tidak peduli seberapa jauh kursor bergerak, sementara opsi lain bervariasi tergantung pada berapa banyak karakter yang harus diproses oleh konsol.
Joel Coehoorn

1
Ini adalah jumlah yang sangat bagus dari berbagai opsi yang tersedia. Namun, saya juga melihat kedipan saat menggunakan \ r. Dengan \ b jelas tidak ada berkedip karena teks fix ("Menghitung:") tidak ditulis ulang. Anda juga akan berkedip jika Anda menambahkan \ b tambahan dan menulis ulang teks perbaikan seperti yang terjadi dengan \ b dan SetCursorPosition. Mengenai komentar Joel: Joel pada dasarnya benar, namun masih akan mengungguli SetCursorPosition pada garis yang sangat panjang, tetapi perbedaannya semakin sedikit.
Dirk Vollmar

27

Anda dapat menggunakan urutan escape \ b (backspace) untuk membuat cadangan sejumlah karakter tertentu pada baris saat ini. Ini hanya memindahkan lokasi saat ini, tidak menghapus karakter.

Sebagai contoh:

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

Di sini, baris adalah garis persentase untuk menulis ke konsol. Caranya adalah dengan menghasilkan jumlah \ b karakter yang benar untuk hasil sebelumnya.

Keuntungan dari ini selama \ r pendekatan adalah bahwa jika karya bahkan jika persentase output tidak di awal baris.


1
+1, ini ternyata menjadi metode tercepat yang disajikan (lihat komentar pengujian saya di bawah)
Kevin

19

\rdigunakan untuk skenario ini.
\r mewakili carriage return yang berarti kursor kembali ke awal baris.
Itu sebabnya Windows menggunakan \n\rsebagai penanda garis baru.
\nmenggerakkan Anda ke bawah garis, dan \rmengembalikan Anda ke awal garis.


22
Kecuali itu sebenarnya \ r \ n.
Joel Mueller

14

Saya hanya harus bermain dengan kelas divo ConsoleSpinner. Milik saya jauh dari ringkas, tetapi tidak cocok bagi saya bahwa pengguna kelas itu harus menulis while(true)loop mereka sendiri . Saya memotret untuk pengalaman yang lebih seperti ini:

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

    // Do some work...

    spin.Stop(); 
}

Dan saya menyadarinya dengan kode di bawah ini. Karena saya tidak ingin Start()metode saya diblokir, saya tidak ingin pengguna harus khawatir tentang menulis while(spinFlag)loop-like, dan saya ingin mengizinkan beberapa pemintal pada saat yang sama saya harus menelurkan utas terpisah untuk menangani pemintalan. Dan itu berarti kodenya harus jauh lebih rumit.

Juga, saya belum melakukan banyak multi-threading sehingga mungkin (bahkan mungkin) bahwa saya telah meninggalkan satu atau tiga bug halus di sana. Tapi sepertinya itu bekerja cukup baik sejauh ini:

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

Modifikasi yang bagus, meskipun kode sampel bukan milik saya. Ini diambil dari blog Brad Abrams (lihat tautan di jawaban saya). Saya pikir ini baru saja ditulis sebagai sampel sederhana yang menunjukkan SetCursorPosition. Btw, saya pasti terkejut (dengan cara positif) tentang diskusi dimulai tentang apa yang saya pikir hanya sampel sederhana. Itu sebabnya saya suka situs ini :-)
Dirk Vollmar

4

Secara eksplisit menggunakan Return Carrage (\ r) di awal baris daripada (secara implisit atau eksplisit) menggunakan Baris Baru (\ n) di akhir harus mendapatkan apa yang Anda inginkan. Sebagai contoh:

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

-1, Pertanyaan meminta C #, saya menulis ulang dalam C # dan Anda mengubahnya kembali ke F #
Malfist

Sepertinya konflik pengeditan alih-alih dia mengubah C # Anda kembali ke F #. Perubahannya satu menit setelah Anda, dan fokus pada sprintf.
Andy

Terima kasih atas hasil editnya. Saya cenderung menggunakan mode interaktif F # untuk menguji berbagai hal dan menganggap bagian penting adalah panggilan BCL, yang sama dalam 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));
    }

1

Dari dokumen Konsol di MSDN:

Anda bisa mengatasi masalah ini dengan mengatur properti TextWriter.NewLine dari properti Keluar atau Kesalahan ke string terminasi baris lain. Sebagai contoh, pernyataan C #, Console.Error.NewLine = "\ r \ n \ r \ n" ;, mengatur string terminasi baris untuk aliran output kesalahan standar ke dua carriage return dan urutan feed line. Kemudian Anda dapat secara eksplisit memanggil metode WriteLine dari objek aliran output kesalahan, seperti dalam pernyataan C #, Console.Error.WriteLine ();

Jadi - saya melakukan ini:

Console.Out.Newline = String.Empty;

Kemudian saya dapat mengontrol output sendiri;

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

Cara lain untuk sampai ke sana.


Anda bisa menggunakan Console.Write () untuk tujuan yang sama, tanpa mendefinisikan ulang properti NewLine ...
Radosław Gers

1

Ini berfungsi jika Anda ingin membuat file menghasilkan terlihat keren.

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

Dan ini adalah metode yang saya dapatkan dari beberapa jawaban di bawah dan memodifikasinya

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

Ini satu lagi: 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

Jika Anda ingin memperbarui satu baris, tetapi informasinya terlalu panjang untuk ditampilkan pada satu baris, mungkin perlu beberapa baris baru. Saya mengalami masalah ini, dan di bawah ini adalah salah satu cara untuk menyelesaikannya.

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

saya sedang mencari solusi yang sama di vb.net dan saya menemukan ini dan itu bagus.

Namun seperti @JohnOdom menyarankan cara yang lebih baik untuk menangani ruang kosong jika yang sebelumnya lebih besar dari yang sekarang ..

saya membuat fungsi di vb.net dan berpikir seseorang bisa dibantu ..

ini kode saya:

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

Di sini Anda dapat menggunakan fitur VB dari variabel statis lokal: Static intLastLength As Integer.
Mark Hurd

0

Saya sedang melakukan pencarian untuk melihat apakah solusi yang saya tulis dapat dioptimalkan untuk kecepatan. Yang saya inginkan adalah penghitung waktu mundur, bukan hanya memperbarui baris saat ini. Inilah yang saya pikirkan. Mungkin bermanfaat bagi seseorang

            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

Terinspirasi oleh @ E.Lahu Solution, implementasi kemajuan bar dengan persentase.

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

Inilah pendapat saya tentang jawaban s soosh dan 0xA3. Itu dapat memperbarui konsol dengan pesan pengguna sambil memperbarui pemintal dan memiliki indikator waktu yang telah berlalu juga.

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

penggunaannya adalah seperti ini:

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

The SetCursorPositionMetode bekerja dalam multi-threading skenario, di mana dua metode lainnya tidak

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.