Related
This question already has an answer here:
Console use CAPS for user input
(1 answer)
Closed 4 years ago.
I wanted to create a little "Terminal"-Game, where everything is in uppercase.
But when you type with console readline, the input isn't all uppercase, and i haven't found a solution yet.
So if i type "login" i want the console to type "LOGIN" in realtime.
At the moment, my code looks like this:
static void Main(string[] args)
{
slowType("TERMINALTEST V1.0 CORP", 50, 100);
Console.WriteLine();
slowType(">", 50, 100);
string temp = Console.ReadLine();
}
static void slowType(string input, int minvelo, int maxvelo)
{
Random temp = new Random();
for (int i = 0; i < input.Length; i++)
{
Console.Write(input[i]);
Thread.Sleep(temp.Next(minvelo, maxvelo));
}
}
and i want to replace
string temp = Console.ReadLine();
with
string temp = MethodForTypingUppercase();
This will take each character 1 at a time, exit if enter is pressed, else we uppercase it, suppress the original character, and then write the uppercased version.
static void Main(string[] args)
{
string test = GetCapitalizedInput();
Console.WriteLine("Captured: " + test);
Console.ReadLine();
}
static string GetCapitalizedInput()
{
string input = "";
while (true)
{
var keypress = Console.ReadKey(true); // suppress original output
if (keypress.Key == ConsoleKey.Enter)
{
break;
}
string uppercased = keypress.KeyChar.ToString().ToUpper();
input += uppercased;
Console.Write(uppercased);
}
return input;
}
I'll keep this succinct. I am learning C# and exploring the possibilities of the language. Being a python programmer by heart, I am fairly new to the .NET realm.
I am currently writing a Towers of Hanoi console application. I already understand the recursion part of the code as that is not challenging.
Here is my current code for my peg class.
namespace Tower_of_hanoi
{
class PegClass
{
private int pegheight;
private int y = 3;
int[] rings = new int[0];
public PegClass()
{
// default constructor
}
public PegClass(int height)
{
pegheight = height;
}
// other functions
public void AddRing(int size)
{
Array.Resize(ref rings, rings.Length + 1);
rings[rings.Length - 1] = size;
}
public void DrawPeg(int x)
{
for (int i = 1; i <= pegheight; i++)
{
Console.SetCursorPosition(x, y);
Console.WriteLine("|");
y++;
}
if (x < 7)
{
x = 7;
}
Console.SetCursorPosition(x - 7, y); // print the base
Console.WriteLine("----------------");
}
}
}
And this is my code for the main class to display the pegs. I have facilitated the printing of the pegs by putting them in a method.
namespace Tower_of_hanoi
{
class Program
{
static void Main(string[] args)
{
PegClass myPeg = new PegClass(8);
PegClass myPeg2 = new PegClass(8);
PegClass myPeg3 = new PegClass(8);
DrawBoard(myPeg, myPeg2, myPeg3);
Console.ReadLine();
}
public static void DrawBoard(PegClass peg1,PegClass peg2,PegClass peg3)
{
Console.Clear();
peg1.DrawPeg(20);
peg2.DrawPeg(40);
peg3.DrawPeg(60);
}
}
}
My question remains,
How does one move "rings" over to "pegs" in a console application? I understand how this would work in WinForms, but I want a challenge.
Thank you everyone in advance,
youmeoutside
All you have to do ,is modify the DrawPeg method to accept the number of current "rings"
public void DrawPeg(int x, int numberOfRings = 0)
{
for (int i = pegheight; i >= 1; i--)
{
string halfRing = new string(' ', i);
if (numberOfRings > 0)
{
if (i <= numberOfRings)
halfRing = new string('-', numberOfRings - i + 1);
}
Console.SetCursorPosition(x - halfRing.Length * 2 + i + (halfRing.Contains("-") ? (-i + halfRing.Length) : 0), y);
Console.WriteLine(halfRing + "|" + halfRing);
y++;
}
if (x < 7)
{
x = 7;
}
Console.SetCursorPosition(x - 7, y); // print the base
Console.WriteLine("----------------");
}
Then ,you can call your DrawBoard method with your current values (Now they are hard-coded)
public static void DrawBoard(PegClass peg1, PegClass peg2, PegClass peg3)
{
Console.Clear();
peg1.DrawPeg(20, 1);
peg2.DrawPeg(40, 2);
peg3.DrawPeg(60, 4);
}
Now all you have to do ,is call the methods with different numbers of rings every time your player makes a move
I'm writing a simple c# console app that uploads files to sftp server. However, the amount of files are large. I would like to display either percentage of files uploaded or just the number of files upload already from the total number of files to be upload.
First, I get all the files and the total number of files.
string[] filePath = Directory.GetFiles(path, "*");
totalCount = filePath.Length;
Then I loop through the file and upload them one by one in foreach loop.
foreach(string file in filePath)
{
string FileName = Path.GetFileName(file);
//copy the files
oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
//Console.WriteLine("Uploading file..." + FileName);
drawTextProgressBar(0, totalCount);
}
In the foreach loop I have a progress bar which I have issues with. It doesn't display properly.
private static void drawTextProgressBar(int progress, int total)
{
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = 32;
Console.Write("]"); //end
Console.CursorLeft = 1;
float onechunk = 30.0f / total;
//draw filled part
int position = 1;
for (int i = 0; i < onechunk * progress; i++)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw unfilled part
for (int i = position; i <= 31 ; i++)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw totals
Console.CursorLeft = 35;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(progress.ToString() + " of " + total.ToString() + " "); //blanks at the end remove any excess
}
The output is just [ ] 0 out of 1943
What am I doing wrong here?
EDIT:
I'm trying to display the progress bar while I'm loading and exporting XML files. However, it's going through a loop. After it finishes the first round it goes to the second and so on.
string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
foreach (string file in xmlFilePath)
{
for (int i = 0; i < xmlFilePath.Length; i++)
{
//ExportXml(file, styleSheet);
drawTextProgressBar(i, xmlCount);
count++;
}
}
It never leaves the for loop...Any suggestions?
I was also looking for a console progress bar. I didn't find one that did what I needed, so I decided to roll my own. Click here for the source code (MIT License).
Features:
Works with redirected output
If you redirect the output of a console application (e.g., Program.exe > myfile.txt), most implementations will crash with an exception. That's because Console.CursorLeft and Console.SetCursorPosition() don't support redirected output.
Implements IProgress<double>
This allows you to use the progress bar with async operations that report a progress in the range of [0..1].
Thread-safe
Fast
The Console class is notorious for its abysmal performance. Too many calls to it, and your application slows down. This class performs only 8 calls per second, no matter how often you report a progress update.
Use it like this:
Console.Write("Performing some task... ");
using (var progress = new ProgressBar()) {
for (int i = 0; i <= 100; i++) {
progress.Report((double) i / 100);
Thread.Sleep(20);
}
}
Console.WriteLine("Done.");
I know this is an old thread, and apologies for the self promotion, however I've recently written an open source console library available on nuget Goblinfactory.Konsole with threadsafe multiple progress bar support, that might help anyone new to this page needing one that doesnt block the main thread.
It's somewhat different to the answers above as it allows you to kick off the downloads and tasks in parallel and continue with other tasks;
cheers, hope this is helpful
A
var t1 = Task.Run(()=> {
var p = new ProgressBar("downloading music",10);
... do stuff
});
var t2 = Task.Run(()=> {
var p = new ProgressBar("downloading video",10);
... do stuff
});
var t3 = Task.Run(()=> {
var p = new ProgressBar("starting server",10);
... do stuff .. calling p.Refresh(n);
});
Task.WaitAll(new [] { t1,t2,t3 }, 20000);
Console.WriteLine("all done.");
gives you this type of output
The nuget package also includes utilities for writing to a windowed section of the console with full clipping and wrapping support, plus PrintAt and various other helpful classes.
I wrote the nuget package because I constantly ended up writing lots of common console routines whenever I wrote build and ops console scripts and utilities.
If I was downloading several files, I used to slowly Console.Write to the screen on each thread, and used to try various tricks to make reading the interleaved output on the screen easier to read, e.g. different colors or numbers. I eventually wrote the windowing library so that output from different threads could simply be printed to different windows, and it cut down a ton of boilerplate code in my utility scripts.
For example, this code,
var con = new Window(200,50);
con.WriteLine("starting client server demo");
var client = new Window(1, 4, 20, 20, ConsoleColor.Gray, ConsoleColor.DarkBlue, con);
var server = new Window(25, 4, 20, 20, con);
client.WriteLine("CLIENT");
client.WriteLine("------");
server.WriteLine("SERVER");
server.WriteLine("------");
client.WriteLine("<-- PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.DarkYellow, "--> PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.Red, "<-- 404|Not Found|some long text to show wrapping|");
client.WriteLine(ConsoleColor.Red, "--> 404|Not Found|some long text to show wrapping|");
con.WriteLine("starting names demo");
// let's open a window with a box around it by using Window.Open
var names = Window.Open(50, 4, 40, 10, "names");
TestData.MakeNames(40).OrderByDescending(n => n).ToList()
.ForEach(n => names.WriteLine(n));
con.WriteLine("starting numbers demo");
var numbers = Window.Open(50, 15, 40, 10, "numbers",
LineThickNess.Double,ConsoleColor.White,ConsoleColor.Blue);
Enumerable.Range(1,200).ToList()
.ForEach(i => numbers.WriteLine(i.ToString())); // shows scrolling
produces this
You can also create progress bars inside a window just as easily as writing to the windows. (mix and match).
You might want to try https://www.nuget.org/packages/ShellProgressBar/
I just stumbled upon this progress bar implementation - its cross platform, really easy to use, quite configurable and does what it should right out of the box.
Just sharing because i liked it a lot.
This line is your problem:
drawTextProgressBar(0, totalCount);
You're saying the progress is zero in every iteration, this should be incremented. Maybe use a for loop instead.
for (int i = 0; i < filePath.length; i++)
{
string FileName = Path.GetFileName(filePath[i]);
//copy the files
oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
//Console.WriteLine("Uploading file..." + FileName);
drawTextProgressBar(i, totalCount);
}
I quite liked the original poster's progress bar, but found that it did not display progress correctly with certain progress/total item combinations. The following, for example, does not draw correctly, leaving an extra grey block at the end of the progress bar:
drawTextProgressBar(4114, 4114)
I re-did some of the drawing code to remove the unnecessary looping which fixed the above issue and also sped things up quite a bit:
public static void drawTextProgressBar(string stepDescription, int progress, int total)
{
int totalChunks = 30;
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = totalChunks + 1;
Console.Write("]"); //end
Console.CursorLeft = 1;
double pctComplete = Convert.ToDouble(progress) / total;
int numChunksComplete = Convert.ToInt16(totalChunks * pctComplete);
//draw completed chunks
Console.BackgroundColor = ConsoleColor.Green;
Console.Write("".PadRight(numChunksComplete));
//draw incomplete chunks
Console.BackgroundColor = ConsoleColor.Gray;
Console.Write("".PadRight(totalChunks - numChunksComplete));
//draw totals
Console.CursorLeft = totalChunks + 5;
Console.BackgroundColor = ConsoleColor.Black;
string output = progress.ToString() + " of " + total.ToString();
Console.Write(output.PadRight(15) + stepDescription); //pad the output so when changing from 3 to 4 digits we avoid text shifting
}
I have copy pasted your ProgressBar method. Because your error was in the loop as the accepted answer mentioned. But the ProgressBar method has some syntax errors too. Here is the working version. Slightly modified.
private static void ProgressBar(int progress, int tot)
{
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = 32;
Console.Write("]"); //end
Console.CursorLeft = 1;
float onechunk = 30.0f / tot;
//draw filled part
int position = 1;
for (int i = 0; i < onechunk * progress; i++)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw unfilled part
for (int i = position; i <= 31; i++)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw totals
Console.CursorLeft = 35;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(progress.ToString() + " of " + tot.ToString() + " "); //blanks at the end remove any excess
}
Please note that #Daniel-wolf has a better approach: https://stackoverflow.com/a/31193455/169714
I've created this handy class that works with System.Reactive. I hope you find it lovely enough.
public class ConsoleDisplayUpdater : IDisposable
{
private readonly IDisposable progressUpdater;
public ConsoleDisplayUpdater(IObservable<double> progress)
{
progressUpdater = progress.Subscribe(DisplayProgress);
}
public int Width { get; set; } = 50;
private void DisplayProgress(double progress)
{
if (double.IsNaN(progress))
{
return;
}
var progressBarLenght = progress * Width;
System.Console.CursorLeft = 0;
System.Console.Write("[");
var bar = new string(Enumerable.Range(1, (int) progressBarLenght).Select(_ => '=').ToArray());
System.Console.Write(bar);
var label = $#"{progress:P0}";
System.Console.CursorLeft = (Width -label.Length) / 2;
System.Console.Write(label);
System.Console.CursorLeft = Width;
System.Console.Write("]");
}
public void Dispose()
{
progressUpdater?.Dispose();
}
}
Console Progress Bar in C# (Complete Code - Just copy and paste on your IDE)
class ConsoleUtility
{
const char _block = '■';
const string _back = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
const string _twirl = "-\\|/";
public static void WriteProgressBar(int percent, bool update = false)
{
if (update)
Console.Write(_back);
Console.Write("[");
var p = (int)((percent / 10f) + .5f);
for (var i = 0; i < 10; ++i)
{
if (i >= p)
Console.Write(' ');
else
Console.Write(_block);
}
Console.Write("] {0,3:##0}%", percent);
}
public static void WriteProgress(int progress, bool update = false)
{
if (update)
Console.Write("\b");
Console.Write(_twirl[progress % _twirl.Length]);
}
}
//This is how to call in your main method
static void Main(string[] args)
{
ConsoleUtility.WriteProgressBar(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgressBar(i, true);
Thread.Sleep(50);
}
Console.WriteLine();
ConsoleUtility.WriteProgress(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgress(i, true);
Thread.Sleep(50);
}
}
I just stumbled upon this thread looking for something else, and I thought I'd drop off my code that I put together that downloads a List of files using the DownloadProgressChanged. I find this super helpful so I not only see the progress, but the actual size as the file is coming through. Hope it helps someone!
public static bool DownloadFile(List<string> files, string host, string username, string password, string savePath)
{
try
{
//setup FTP client
foreach (string f in files)
{
FILENAME = f.Split('\\').Last();
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
wc.DownloadFileAsync(new Uri(host + f), savePath + f);
while (wc.IsBusy)
System.Threading.Thread.Sleep(1000);
Console.Write(" COMPLETED!");
Console.WriteLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
return true;
}
private static void ProgressChanged(object obj, System.Net.DownloadProgressChangedEventArgs e)
{
Console.Write("\r --> Downloading " + FILENAME +": " + string.Format("{0:n0}", e.BytesReceived / 1000) + " kb");
}
private static void Completed(object obj, AsyncCompletedEventArgs e)
{
}
Here's an example of the output:
Hope it helps someone!
I am still a little new to C# but I believe the below might help.
string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
int count = 0;
foreach (string file in xmlFilePath)
{
//ExportXml(file, styleSheet);
drawTextProgressBar(count, xmlCount);
count++;
}
Based on all posts above, I did an improved version.
No jumping cursors. It's invisible now.
Improved performance (costs 1/5~1/10 time of the origin one).
Interface based. Easy to move to something else.
public class ConsoleProgressBar : IProgressBar
{
private const ConsoleColor ForeColor = ConsoleColor.Green;
private const ConsoleColor BkColor = ConsoleColor.Gray;
private const int DefaultWidthOfBar = 32;
private const int TextMarginLeft = 3;
private readonly int _total;
private readonly int _widthOfBar;
public ConsoleProgressBar(int total, int widthOfBar = DefaultWidthOfBar)
{
_total = total;
_widthOfBar = widthOfBar;
}
private bool _intited;
public void Init()
{
_lastPosition = 0;
//Draw empty progress bar
Console.CursorVisible = false;
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = _widthOfBar;
Console.Write("]"); //end
Console.CursorLeft = 1;
//Draw background bar
for (var position = 1; position < _widthOfBar; position++) //Skip the first position which is "[".
{
Console.BackgroundColor = BkColor;
Console.CursorLeft = position;
Console.Write(" ");
}
}
public void ShowProgress(int currentCount)
{
if (!_intited)
{
Init();
_intited = true;
}
DrawTextProgressBar(currentCount);
}
private int _lastPosition;
public void DrawTextProgressBar(int currentCount)
{
//Draw current chunk.
var position = currentCount * _widthOfBar / _total;
if (position != _lastPosition)
{
_lastPosition = position;
Console.BackgroundColor = ForeColor;
Console.CursorLeft = position >= _widthOfBar ? _widthOfBar - 1 : position;
Console.Write(" ");
}
//Draw totals
Console.CursorLeft = _widthOfBar + TextMarginLeft;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(currentCount + " of " + _total + " "); //blanks at the end remove any excess
}
}
public interface IProgressBar
{
public void ShowProgress(int currentCount);
}
And some test code:
var total = 100;
IProgressBar progressBar = new ConsoleProgressBar(total);
for (var i = 0; i <= total; i++)
{
progressBar.ShowProgress(i);
Thread.Sleep(50);
}
Thread.Sleep(500);
Console.Clear();
total = 9999;
progressBar = new ConsoleProgressBar(total);
for (var i = 0; i <= total; i++)
{
progressBar.ShowProgress(i);
}
This is a program with two threads; one for output and one for input. (Where _ is the console cursor)
Please enter a number:
12_
While you're typing 12, output gets generated which clears the current line and writes over it, so this happens:
Please enter a number:
Output
_
How can I make it take the 12 which you're still entering and move it to the next line, so you don't have to retype it?
Thanks in advance.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
Program prog = new Program();
Thread t1 = new Thread(prog.getInput);
t1.Start();
prog.otherThread();
}
public void otherThread()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
ClearCurrentConsoleLine();
Console.WriteLine("Output");
}
}
public void getInput()
{
while (true)
{
string msg;
msg = Console.ReadLine();
}
}
public static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
for (int i = 0; i < Console.WindowWidth; i++)
{
Console.Write(" ");
}
Console.SetCursorPosition(0, currentLineCursor);
}
}
As you can see, when you enter "Hello" and DON'T enter, after 3 seconds it will be overwritten by "Output". I want to move the "Hello" and the input to the second line before it gets overwritten.
I just found this article (web archive) where Cursor positions and modifications are discussed. I found it pretty straight forward.
The centerpiec of it would be:
int left = Console.CursorLeft;
int top = Console.CursorTop;
Console.SetCursorPosition(15, 20);
If I understood you correctly, this should work.
Make the string msg Global. Declare it outside all functions.
Now simply print it in the next line after you clear the previous line..
class Program
{
string msg;
static void Main(string[] args)
{
Program prog = new Program();
Thread t1 = new Thread(prog.getInput);
t1.Start();
prog.otherThread();
}
public void otherThread()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
ClearCurrentConsoleLine();
Console.WriteLine("Output");
}
}
public void getInput()
{
while (true)
{
msg = Console.ReadLine();
}
}
public static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
for (int i = 0; i < Console.WindowWidth; i++)
{
Console.Write(" ");
}
Console.SetCursorPosition( 0, currentLineCursor + 1 );
Console.Write(msg);
Console.SetCursorPosition(0, currentLineCursor);
}
}
I just want to know how to create simple animations like blinking, moving stuffs on C# console applications. Is there any special method for this?
Traditional Console Spinner:
static void Main(string[] args)
{
ConsoleSpiner spin = new ConsoleSpiner();
Console.Write("Working....");
while (true)
{
spin.Turn();
}
}
public class ConsoleSpiner
{
int counter;
public ConsoleSpiner()
{
counter = 0;
}
public void Turn()
{
counter++;
switch (counter % 4)
{
case 0: Console.Write("/"); break;
case 1: Console.Write("-"); break;
case 2: Console.Write("\\"); break;
case 3: Console.Write("|"); break;
}
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
}
}
Here is my spinner. The purpose of it is to have a program do some work while the spinner displays to the user that something is actually happening:
public class Spinner : IDisposable
{
private const string Sequence = #"/-\|";
private int counter = 0;
private readonly int left;
private readonly int top;
private readonly int delay;
private bool active;
private readonly Thread thread;
public Spinner(int left, int top, int delay = 100)
{
this.left = left;
this.top = top;
this.delay = delay;
thread = new Thread(Spin);
}
public void Start()
{
active = true;
if (!thread.IsAlive)
thread.Start();
}
public void Stop()
{
active = false;
Draw(' ');
}
private void Spin()
{
while (active)
{
Turn();
Thread.Sleep(delay);
}
}
private void Draw(char c)
{
Console.SetCursorPosition(left, top);
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(c);
}
private void Turn()
{
Draw(Sequence[++counter % Sequence.Length]);
}
public void Dispose()
{
Stop();
}
}
And you use the class like this:
using (var spinner = new Spinner(10, 10))
{
spinner.Start();
// Do your work here instead of sleeping...
Thread.Sleep(10000);
}
Yes, there are quite a few methods for this.
In particular, you may want to look at the following Console methods:
SetCursorPosition (you can move the cursor around, and overwrite elements)
MoveBufferArea (copy/paste over the top of regions)
ForegroundColor and BackgroundColor (change coloring)
Great work with the ConsoleSpinner and the sequence implementation. Thanks for the code. I thought about sharing my customized approach.
public class ConsoleSpinner
{
static string[,] sequence = null;
public int Delay { get; set; } = 200;
int totalSequences = 0;
int counter;
public ConsoleSpinner()
{
counter = 0;
sequence = new string[,] {
{ "/", "-", "\\", "|" },
{ ".", "o", "0", "o" },
{ "+", "x","+","x" },
{ "V", "<", "^", ">" },
{ ". ", ".. ", "... ", "...." },
{ "=> ", "==> ", "===> ", "====>" },
// ADD YOUR OWN CREATIVE SEQUENCE HERE IF YOU LIKE
};
totalSequences = sequence.GetLength(0);
}
/// <summary>
///
/// </summary>
/// <param name="sequenceCode"> 0 | 1 | 2 |3 | 4 | 5 </param>
public void Turn(string displayMsg = "", int sequenceCode = 0)
{
counter++;
Thread.Sleep(Delay);
sequenceCode = sequenceCode > totalSequences - 1 ? 0 : sequenceCode;
int counterValue = counter % 4;
string fullMessage = displayMsg + sequence[sequenceCode, counterValue];
int msglength = fullMessage.Length;
Console.Write(fullMessage);
Console.SetCursorPosition(Console.CursorLeft - msglength, Console.CursorTop);
}
}
Implementation:
ConsoleSpinner spinner = new ConsoleSpinner();
spinner.Delay = 300;
while (true)
{
spinner.Turn(displayMsg: "Working ",sequenceCode:5);
}
Output:
You'll need to use Console.ForegroundColor, Console.BackgroundColor and Console.SetCursorPosition(int, int)
EDIT: For inspiration, Let's Dance
Just saw this and a few other threads about it and love this kind of stuff! I took Tuukka's nice piece of code and improved it a bit so the class can easily be set to just about any spin sequence. I'll probably add some accessors and an overloaded constructor to polish it off and put it in the ol' toolbox. Fun stuff!
class ConsoleSpinner
{
int counter;
string[] sequence;
public ConsoleSpinner()
{
counter = 0;
sequence = new string[] { "/", "-", "\\", "|" };
sequence = new string[] { ".", "o", "0", "o"};
sequence = new string[] { "+", "x" };
sequence = new string[] { "V", "<", "^", ">" };
sequence = new string[] { ". ", ".. ", "... ", "...." };
}
public void Turn()
{
counter++;
if (counter >= sequence.Length)
counter = 0;
Console.Write(sequence[counter]);
Console.SetCursorPosition(Console.CursorLeft - sequence[counter].Length, Console.CursorTop);
}
}
I took the anwser from #ThisGuy and modified it a bit, but I noticed, that sometimes the row is not cleared as it should.
static void Main(string[] args)
{
Console.WriteLine("1");
var tddf = XConsole.BusyIndicator("test 0 trtg fdfdfvdgd 4343", () =>
{
return "";
});
Console.WriteLine("2");
var tdd = XConsole.BusyIndicator("test 0 trtg fdfdfvdgd", () =>
{
Thread.Sleep(1);
return "";
});
Console.WriteLine("3");
var t = XConsole.BusyIndicator("test 1 trtg vdgd", () =>
{
Thread.Sleep(1000);
return "";
});
Console.WriteLine("4");
var xt = XConsole.BusyIndicator("test 2", () =>
{
Thread.Sleep(2000);
return "";
});
var xtx = XConsole.BusyIndicator("test 2 csds fsd fdsf ds s", () =>
{
Thread.Sleep(2000);
return "";
});
Console.WriteLine("5");
Thread.Sleep(4000);
}
Spinner class:
public class Spinner : IDisposable
{
private const string Sequence1 = #"/-\|";
private const string Sequence3 = #".o0o";
private const string Sequence2 = #"<^>v";
private const string Sequence4 = #"#■.";
private const string Sequence5 = #"▄▀";
private const string Sequence = #"└┘┐┌";
private string BusyMessage = "";
private int counter = 0;
private readonly int delay;
private bool active;
private readonly Thread thread;
public Spinner(int delay = 200)
{
this.delay = delay;
thread = new Thread(Spin);
}
public void Start()
{
active = true;
Console.CursorVisible = false;
if (!thread.IsAlive)
{
thread.Start();
}
}
public void Start(string busyMessage)
{
BusyMessage = busyMessage;
Start();
}
public void Stop()
{
active = false;
Console.CursorVisible = true;
ClearCurrentConsoleLine();
BusyMessage = "";
}
private static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition(0, currentLineCursor);
}
private void Spin()
{
while (active)
{
Turn();
Thread.Sleep(delay);
}
}
/// <summary>
/// Draws the busy indicator
/// </summary>
/// <param name="c">if empty char, then clear screen</param>
private void Draw(char c)
{
int left = Console.CursorLeft;
int top = Console.CursorTop;
Console.Write('[');
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(c);
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write(']');
if (!string.IsNullOrEmpty(BusyMessage))
{
Console.Write(" " + BusyMessage);
}
//reset cursor position
Console.SetCursorPosition(left, top);
}
private void Turn()
{
Draw(Sequence[++counter % Sequence.Length]);
}
public void Dispose()
{
Stop();
}
}
My console class:
public static class XConsole {
public static T BusyIndicator<T>(Func<T> action)
{
T result;
using (var spinner = new Spinner())
{
spinner.Start();
result = action();
spinner.Stop();
}
return result;
}
public static T BusyIndicator<T>(string content, Func<T> action)
{
T result;
using (var spinner = new Spinner())
{
spinner.Start(content);
result = action();
spinner.Stop();
}
return result;
}
}
Why do I see this result sometimes?
1
2
3 st 0 trtg fdfdfvdgd ]
4
[└] test 2 csds fsd fdsf ds s
Looks like the Dispose didn't trigger? Or a new Task alredy started?
It should look like this:
1
2
3
4
[└] test 2 csds fsd fdsf ds s
UPDATE:
I added ClearCurrentConsoleLine(); and changed the Draw method, this fixed the issue.
i'm not TOO sure i know exactly what you're on about. but i'll give it a shot.
I think the biggest and best way to cause the "blinking" affect (or what i think the blinking affect is) is to use a carriage return.
The best way to explain it to you is to show you the Foo Bar experiment.
start a new project, and in your Main function, try this.
Console.WriteLine("Foo/nBar");
The output will look like this
Foo
Bar
But if you use a carriage return.
Console.WriteLine("Foo/rBar");
The output will look like this
Bar
The reason is that Foo is written, then the carriage return takes you BACK to the start of the line, then Bar is written. All you ever see is Bar.
This can be helpful for "Moving" things on one line, instead of rewriting the same things again on multiple lines.
A way to do progression would be to use Console.Write();
Try this.
Console.Write("Loading");
for(int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
Console.Write(".");
}
The output should be
Loading
Followed by a Fullstop every second for 10 seconds.
If you combine the Carriage return with the Console.Write(); function you can write multiple things on a single line, clear the line and write something else, or indeed, the same thing just moved slightly. (This would of course need more than i have shown you, like recording where the "object" you are controlling is situated. If you would like a short example i would be happy to do one, just comment and ask me for it :)
Edit:
I noticed people mentioning colour, which i forgot. If you were doing animation i guess colour would be a must.
ForegroundColor and BackgroundColor are where it's at.
note that ForegroundColor will apply to the next characters written to the console, it will not completely recolour the Console.
/Edit
I hope this helps,
Skintkingle ;)
I thought I'd chime in with my version of the previously listed code. Here it is:
class ConsoleSpinner
{
bool increment = true,
loop = false;
int counter = 0;
int delay;
string[] sequence;
public ConsoleSpinner(string sSequence = "dots", int iDelay = 100, bool bLoop = false)
{
delay = iDelay;
if (sSequence == "dots")
{
sequence = new string[] { ". ", ".. ", "... ", "...." };
loop = true;
}
else if (sSequence == "slashes")
sequence = new string[] { "/", "-", "\\", "|" };
else if (sSequence == "circles")
sequence = new string[] { ".", "o", "0", "o" };
else if (sSequence == "crosses")
sequence = new string[] { "+", "x" };
else if (sSequence == "arrows")
sequence = new string[] { "V", "<", "^", ">" };
}
public void Turn()
{
if (loop)
{
if (counter >= sequence.Length - 1)
increment = false;
if (counter <= 0)
increment = true;
if (increment)
counter++;
else if (!increment)
counter--;
}
else
{
counter++;
if (counter >= sequence.Length)
counter = 0;
}
Console.Write(sequence[counter]);
Console.SetCursorPosition(Console.CursorLeft - sequence[counter].Length, Console.CursorTop);
System.Threading.Thread.Sleep(delay);
}
}
Adds delay (unfortunately through Thread.Sleep() but, hey), the ability to loop the animation forwards and backwards (starts to reverse when it hits the end), and general improvements overall. Enjoy!
This would be my prefered method:
public sealed class Spinner
{
private static Lazy<Spinner> lazy =
new Lazy<Spinner>(()=> new Spinner());
public static void Reset()
{
lazy = new Lazy<Spinner>(()=> new Spinner());
}
public static Spinner Instance { get { return lazy.Value; }}
private readonly int _consoleX;
private readonly int _consoleY;
private readonly char[] _frames = { '|', '/', '-', '\\' };
private int _current;
private Spinner()
{
_current = 0;
_consoleX = Console.CursorLeft;
_consoleY = Console.CursorTop;
}
public void Update()
{
Console.Write(_frames[_current]);
Console.SetCursorPosition(_consoleX, _consoleY);
if (++_current >= _frames.Length)
_current = 0;
}
}
Call Spinner.Instance.Update() to start the spinner at the current position of the console. Any consecutive call will render the next frame at the same position.
Call Spinner.Reset() if you want to write more text and then add a new spinner at a new location.
This is the way i solved it, sure it could/can be shorter but, Im just not that smart yet :-)
void cc() { Console.Clear(); }
void cr() { Console.ReadKey(); }
byte Sleeptimer = 90;
void sleepy() { System.Threading.Thread.Sleep(Sleeptimer); }
string[] Loading = { #"-- ", #"\ ", #"| ", #"/ ", "Loading", " complete!" };
for (byte i = 0; i <= 15; i++)
{
cc();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(Loading[0] + Loading[4]);
sleepy();
cc();
Console.WriteLine(Loading[1] + Loading[4]);
sleepy();
cc();
Console.WriteLine(Loading[2] + Loading[4]);
sleepy();
cc();
Console.WriteLine(Loading[3] + Loading[4]);
sleepy();
cc();
if (i == 15) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(Loading[4] + Loading[5]);
cc();
Console.ForegroundColor = ConsoleColor.Cyan;
Next();
}
}
/*
Now i feel even more noob while reading your code.
I'm a newbie in programing.
Guess this way would work if i just incfement the index of each Loading down below right?
Learned too much today, forgot how this exactly works, to increment or to change the index i'd lile to acces
Console.WriteLine(Loading[2] + Loading[4]);
sleepy();
cc();
*/
This is animation with cancellationToken
private static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition(0, currentLineCursor);
}
public static async Task Start(CancellationToken ct)
{
await Task.Run(() =>
{
Thread.Sleep(2000);
Console.WriteLine();
string text = "Executing";
for (int i = 0; i <= 3; i++)
{
if (ct.IsCancellationRequested)
{
Console.SetCursorPosition(0, Console.CursorTop - 1);
ClearCurrentConsoleLine();
ct.ThrowIfCancellationRequested();
}
Thread.Sleep(1000);
Console.SetCursorPosition(0, Console.CursorTop - 1);
ClearCurrentConsoleLine();
Console.WriteLine(text);
if (i == 3)
{
text = "Executing";
i = 0;
}
text += ".";
}
});
}```