If any of you have been following my slow learning process, you'll know what's going on.
I created a border rectangle that looks like this:
+--------+
| |
| |
| |
+--------+
Are you ready to play hangman? yes/no:
Granted, it's bigger and more rectangular.
Unfortunately, with the code I have now, what used to have the words below the rectangle, now have them in the middle, like this:
+--------+
|abcdefghi... |
Are you ready . . .
| |
| |
+--------+
Now, the box is big enough to fit all the words into it, but the initiating question should be below the field. It all started when I called the alphabet into an array, then had it display at specific coordinates.
Here is my code:
namespace Hangman
{
class Program
{
protected static int firstColumn;
protected static int firstRow;
protected static void headerWindow(string border, int posX, int posY)
{
try
{
Console.SetCursorPosition(firstColumn + posX, firstRow + posY);
Console.Write(border);
}
catch (ArgumentOutOfRangeException error)
{
Console.Clear();
Console.Write(error.Message);
}
}
private static void printWord()
{
String[] myWordArrays = File.ReadAllLines("WordList.txt");
Random randomWord = new Random();
//int lineCount = File.ReadLines("WordList.txt").Count();
int activeWord = randomWord.Next(0, myWordArrays.Length);
string userSelection = "";
Console.WriteLine("Are you Ready to play Hangman? yes/no: ");
userSelection = Console.ReadLine();
if (userSelection == "yes")
{
foreach (char letter in myWordArrays[activeWord])
{
Console.Write("_ ");
}
Console.WriteLine("\n \nCan you guess what this " + myWordArrays[activeWord].Length + " letter word is?");
Console.ReadLine();
}
else if (userSelection == "no")
{
Console.WriteLine("I'm sorry you feel that way. Press Enter to Exit the program!");
Console.ReadLine();
}
}
//THIS IS THE CREATION OF THE RECTANGLE!!!
private static void headerFile()
{
Console.Clear();
firstColumn = Console.CursorLeft;
firstRow = Console.CursorTop;
int HEADER_HEIGHT = 6;
int columnNumber = Console.WindowWidth - 1;
var xcoord = 0;
var ycoord = 0;
for (int i = 0; i < columnNumber; i++)
{
headerWindow("-", i, 0);
headerWindow("-", i, HEADER_HEIGHT);
}
for (int i = 0; i < HEADER_HEIGHT; i++)
{
headerWindow("|", 0, i);
headerWindow("|", columnNumber, i);
}
headerWindow("+", xcoord = 0, ycoord = 0);
headerWindow("+", xcoord = columnNumber, ycoord = 0);
headerWindow("+", xcoord = 0, ycoord = 6);
headerWindow("+", xcoord = columnNumber, ycoord = 6);
}
//THIS IS THE CREATION OF THE LIST OF UNUSED CHARACTERS FOR THE GAME
private static void letterChoices()
{
string[] alphabetSelection = File.ReadAllLines("alphabet.txt");
for (int i = 0; i < alphabetSelection.Length; i++)
{
headerWindow(alphabetSelection[i] + " ", i + 1, 1);
Console.WriteLine("\n ");
}
//return;
}
//SHOULD I HAVE MORE HERE??
static void Main(string[] args)
{
headerFile();
letterChoices();
printWord();
}
}
}
I would appreciate not being given the answer as I really need to figure it out for myself, but I've moved the method call from main to headerwindow(), and I've even written it all out separately and in different ways. Ugh!
Please help!
Since you asked not for a direct answer: The fix belongs in the method headerWindow. The same fix could also be applied in letterChoices, depending on your preference.
When the method headFile() finishes, the cursor is blinking at the bottom left of the box (in the 'correct' position to write text)
After your loop in letterChoices, your cursor is just below the alphabet, because it asked headerWindow to write in a certain position.
Something should be changed in headerWindow so that it doesn't keep the cursor where it just wrote to. You have used every property/method needed to create this fix, so it's not some arcane fix or hidden method on Console.
Well the rectangle is created with what looks to be absolute positioning, but the text is based on relative positioning (i.e. it will print wherever the cursor is)
You can either look at making sure the header creation leaves the cursor where you want it OR make the other two methods also absolute positioning.
You need to move the cursor before writing "Are you Ready to play Hangman?".
You can use :
Console.SetCursorPosition(column, row);
you can set the cursor at (o, HeaderHeight+1) before writing the 'Are You..' Message in printWord() method.
Console.SetCursorPosition(0, hh + 1);
It's all about positions ...
What you need is to create a method that prints text using absolute positioning and call it like PrintText("Hello", 20, 15);
So I have fixed it, and wanted to update this question with the completed code for anyone else that had this same issue:
private static void letterChoices()
{
string[] alphabetSelection = File.ReadAllLines("alphabet.txt");
for (int i = 0; i < alphabetSelection.Length; i++)
{
//headerWindow("|", 0, 3);
headerWindow(alphabetSelection[i], i*3+1, 1);
//Console.Write("\n ");
Console.SetCursorPosition(0, 7);
}
It now works properly, without any 'extra' spaces between the "|" on the sides and it properly places the first 'question' underneath the header. :)
Related
When building a Windows Console App in C#, is it possible to write to the console without having to extend a current line or go to a new line? For example, if I want to show a percentage representing how close a process is to completion, I'd just like to update the value on the same line as the cursor, and not have to put each percentage on a new line.
Can this be done with a "standard" C# console app?
If you print only "\r" to the console the cursor goes back to the beginning of the current line and then you can rewrite it. This should do the trick:
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
Notice the few spaces after the number to make sure that whatever was there before is erased.
Also notice the use of Write() instead of WriteLine() since you don't want to add an "\n" at the end of the line.
You can use Console.SetCursorPosition to set the position of the cursor and then write at the current position.
Here is an example showing a 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);
}
}
Note that you will have to make sure to overwrite any existing output with new output or blanks.
Update: As it has been criticized that the example moves the cursor only back by one character, I will add this for clarification: Using SetCursorPosition you may set the cursor to any position in the console window.
Console.SetCursorPosition(0, Console.CursorTop);
will set the cursor to the beginning of the current line (or you can use Console.CursorLeft = 0 directly).
So far we have three competing alternatives for how to do this:
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
}
I've always used Console.CursorLeft = 0, a variation on the third option, so I decided to do some tests. Here's the code I used:
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);
}
On my machine, I get the following results:
Backspaces: 25.0 seconds
Carriage Returns: 28.7 seconds
SetCursorPosition: 49.7 seconds
Additionally, SetCursorPosition caused noticeable flicker that I didn't observe with either of the alternatives. So, the moral is to use backspaces or carriage returns when possible, and thanks for teaching me a faster way to do this, SO!
Update: In the comments, Joel suggests that SetCursorPosition is constant with respect to the distance moved while the other methods are linear. Further testing confirms that this is the case, however constant time and slow is still slow. In my tests, writing a long string of backspaces to the console is faster than SetCursorPosition until somewhere around 60 characters. So backspace is faster for replacing portions of the line shorter than 60 characters (or so), and it doesn't flicker, so I'm going to stand by my initial endorsement of \b over \r and SetCursorPosition.
You can use the \b (backspace) escape sequence to backup a particular number of characters on the current line. This just moves the current location, it does not remove the characters.
For example:
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);
}
Here, line is the percentage line to write to the console. The trick is to generate the correct number of \b characters for the previous output.
The advantage of this over the \r approach is that if works even if your percentage output is not at the beginning of the line.
\r is used for these scenarios.
\r represents a carriage return which means the cursor returns to the start of the line.
That's why Windows uses \n\r as its new line marker.
\n moves you down a line, and \r returns you to the start of the line.
I just had to play with the divo's ConsoleSpinner class. Mine is nowhere near as concise, but it just didn't sit well with me that users of that class have to write their own while(true) loop. I'm shooting for an experience more like this:
static void Main(string[] args)
{
Console.Write("Working....");
ConsoleSpinner spin = new ConsoleSpinner();
spin.Start();
// Do some work...
spin.Stop();
}
And I realized it with the code below. Since I don't want my Start() method to block, I don't want the user to have to worry about writing a while(spinFlag) -like loop, and I want to allow multiple spinners at the same time I had to spawn a separate thread to handle the spinning. And that means the code has to be a lot more complicated.
Also, I haven't done that much multi-threading so it's possible (likely even) that I've left a subtle bug or three in there. But it seems to work pretty well so far:
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();
}
}
Explicitly using a Carrage Return (\r) at the beginning of the line rather than (implicitly or explicitly) using a New Line (\n) at the end should get what you want. For example:
void demoPercentDone() {
for(int i = 0; i < 100; i++) {
System.Console.Write( "\rProcessing {0}%...", i );
System.Threading.Thread.Sleep( 1000 );
}
System.Console.WriteLine();
}
public void Update(string data)
{
Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
Console.Write(string.Format("\r{0}", data));
}
From the Console docs in MSDN:
You can solve this problem by setting
the TextWriter.NewLine property of the
Out or Error property to another line
termination string. For example, the
C# statement, Console.Error.NewLine =
"\r\n\r\n";, sets the line termination
string for the standard error output
stream to two carriage return and line
feed sequences. Then you can
explicitly call the WriteLine method
of the error output stream object, as
in the C# statement,
Console.Error.WriteLine();
So - I did this:
Console.Out.Newline = String.Empty;
Then I am able to control the output myself;
Console.WriteLine("Starting item 1:");
Item1();
Console.WriteLine("OK.\nStarting Item2:");
Another way of getting there.
This works if you want to make generating files look cool .
int num = 1;
var spin = new ConsoleSpinner();
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("");
while (true)
{
spin.Turn();
Console.Write("\r{0} Generating Files ", num);
num++;
}
And this is the method that i got from some answer below and modified it
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);
}
}
Here's another one :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]);
}
}
}
If you want update one line, but the information is too long to show on one line, it may need some new lines. I've encountered this problem, and below is one way to solve this.
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");
}
}
i was looking for same solution in vb.net and i found this one and it's great.
however as #JohnOdom suggested a better way to handle the blanks space if previous one is larger than current one..
i make a function in vb.net and thought someone could get helped ..
here is my 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
I was doing a search for this to see if the solution I wrote could be optimised for speed. What I wanted was a countdown timer, not just updating the current line.
Here's what I came up with. Might be useful to someone
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("");
Inspired by #E.Lahu Solution, implementation of a bar progress with percentage.
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);
}
}
Here is my take on s soosh's and 0xA3's answers.
It can update the console with user messages while updating the spinner and has an elapsed time indicator aswell.
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();
}
}
usage is something like this:
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.");
}
}
The SetCursorPosition method works in multi-threading scenario, where the other two methods don't
I'd like to achieve something like this:
Time consuming operation...OK
Another time consuming operation...
And another one, but it completed, so...OK
I displayed 3 line of text, each one related with a thread which can end sooner or later. But if the second one complete later than the third one, I'll get something like this:
Time consuming operation...OK
Another time consuming operation...
And another one, but it completed, so...OKOK
Which is of course unacceptable. I know how to go back in current line, but is there a way to go UP? I'd swear I've seen it somewhere, though it could be a Linux console :)
Forget it. See Far File Manager! It works in Windows console, it works even in PowerShell! How to make something like this? And the coolest part is it restores console state after exiting. So maybe I should ask - how to access console buffer directly? I assume I'll need some native code to do the trick, but maybe there's another way? I thought of clearing console with each update, but this seems like overkill. Or maybe it isn't? Will it blink?
You can move cursor wherever you want: Console.SetCursorPosition or use Console.CursorTop.
Console.SetCursorPosition(0, Console.CursorTop -1);
Console.WriteLine("Over previous line!!!");
Use a carriage return. This sample prints a single line, overwriting what was there before.
Console.WriteLine();
for (int i = 0; i <= 100; i++)
{
System.Threading.Thread.Sleep(10);
Console.Write("\x000DProgress: " + i);
}
This works as long as all your strings are less than 80 columns (or whatever your terminal buffer is set to).
Note: the following answer was originally edited into the question by the OP.
Here's complete solution with demo:
using System;
using System.Collections.Generic;
using System.Threading;
namespace PowerConsole {
internal class Containers {
internal struct Container {
public int Id;
public int X;
public int Y;
public string Content;
}
public static List<Container> Items = new List<Container>();
private static int Identity = 0;
public static int Add(string text) {
var c = new Container();
c.Id = Identity++;
c.X = Console.CursorLeft;
c.Y = Console.CursorTop;
c.Content = text;
Console.Write(text);
Items.Add(c);
return c.Id;
}
public static void Remove(int id) {
Items.RemoveAt(id);
}
public static void Replace(int id, string text) {
int x = Console.CursorLeft, y = Console.CursorTop;
Container c = Items[id];
Console.MoveBufferArea(
c.X + c.Content.Length, c.Y,
Console.BufferWidth - c.X - text.Length, 1,
c.X + text.Length, c.Y
);
Console.CursorLeft = c.X;
Console.CursorTop = c.Y;
Console.Write(text);
c.Content = text;
Console.CursorLeft = x;
Console.CursorTop = y;
}
public static void Clear() {
Items.Clear();
Identity = 0;
}
}
internal class Program {
private static List<Thread> Threads = new List<Thread>();
private static void Main(string[] args) {
Console.WriteLine("So we have some threads:\r\n");
int i, id;
Random r = new Random();
for (i = 0; i < 10; i++) {
Console.Write("Starting thread " + i + "...[");
id = Containers.Add("?");
Console.WriteLine("]");
Thread t = new Thread((object data) => {
Thread.Sleep(r.Next(5000) + 100);
Console.ForegroundColor = ConsoleColor.Green;
Containers.Replace((int)data, "DONE");
Console.ResetColor();
});
Threads.Add(t);
}
Console.WriteLine("\n\"But will it blend?\"...");
Console.ReadKey(true);
i = 0;
Threads.ForEach(t => t.Start(i++));
Threads.ForEach(t => t.Join());
Console.WriteLine("\r\nVoila.");
Console.ReadKey(true);
}
}
}
I'm about to pull my hair out. I've been trying to get this to work, but all I can come up with is a console screen that looks like this at the top:
+-------------------------------------------------------------------------------------------+
+
+
+
+
+
Are you ready to play Hangman? yes/no:
What I'm trying to do is have Pipes on the short sides of the rectangle at the top (6 rows in length) with '+' on each of the four corners, as well as "----" across the bottom (just as it is currently at the top). I've written this thing 40 different ways but I seem to keep getting the same result. And it seems like it should be easier than writing each stinking line.
So here's my code (I have hidden the unnecessary portions of the code):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hangman
{
class Program
{
protected static int firstColumn;
protected static int firstRow;
protected static void headerWindow( string border, int posX, int posY)
{
try
{
Console.SetCursorPosition(firstColumn + posX, firstRow + posY);
Console.Write(border);
}
catch(ArgumentOutOfRangeException error)
{
Console.Clear();
Console.Write(error.Message);
}
}
//I created a method to perform this, so that I can have the program stay open for either 1) a new game or 2) just to see if I could do it. It works!
private static void printWord()
{
String[] myWordArrays = File.ReadAllLines("WordList.txt");
Random randomWord = new Random();
//int lineCount = File.ReadLines("WordList.txt").Count();
int activeWord = randomWord.Next(0, myWordArrays.Length);
string userSelection = "";
Console.WriteLine("Are you Ready to play Hangman? yes/no: ");
userSelection = Console.ReadLine();
if(userSelection == "yes")
{
//This runs through the randomly chosen word and prints an underscore in place of each letter - it does work
foreach(char letter in myWordArrays[activeWord])
{
Console.Write("_ ");
}
//This prints to the console "Can you guess what this 'varyingLength' letter word is?" - it does work.
Console.WriteLine("\n \nCan you guess what this "+ myWordArrays[activeWord].Length +" letter word is?");
Console.ReadLine();
}
else if(userSelection=="no")
{
Console.WriteLine("I'm sorry you feel that way. Press Enter to Exit the program!");
Console.ReadLine();
}
}
private static void headerFile()
{
Console.Clear();
firstColumn = Console.CursorLeft;
firstRow = Console.CursorTop;
int columnNumber = Console.WindowWidth - 1;
var xcoord = 0;
var ycoord = 0;
if(xcoord==0 && ycoord==0)
{
headerWindow("+",xcoord,ycoord);
Console.Write("");
xcoord++;
}
}
static void Main(string[] args)
{
headerFile();
printWord();
}
}
}
I need to point out that if you see multiples of the same thing, it's because I have commented and hid the long lines of code (in case I want to use it later). It was supposed to stay hidden, but apparently Ctrl+A,C copies even the collapsed text in the IDE.
Try this code out, I think it does what you need:
for(int i = 0; i < columnNumber; i++) // Loop across long length
{
headerWindow("-", i, 0); // Top line
headerWindow("-", i, 6); // Bottom line
}
for(int i = 0; i < 6; i++) // Loop across short length
{
headerWindow("|", 0, i); // Left side
headerWindow("|", columnNumber, i); // Right side
}
// Draw over corners with "+"
headerWindow("+", 0, 0);
headerWindow("+", 0, 6);
headerWindow("+", columnNumber, 0);
headerWindow("+", columnNumber, 6);
Output:
+------------------------------------------------------------------------------+
| |
| |
| |
| |
| |
+------------------------------------------------------------------------------+
It is intended to go in headerFile(). Also you should replace 6 with a constant called HEADER_HEIGHT or something similar for easy modification and readability.
I'd like to achieve something like this:
Time consuming operation...OK
Another time consuming operation...
And another one, but it completed, so...OK
I displayed 3 line of text, each one related with a thread which can end sooner or later. But if the second one complete later than the third one, I'll get something like this:
Time consuming operation...OK
Another time consuming operation...
And another one, but it completed, so...OKOK
Which is of course unacceptable. I know how to go back in current line, but is there a way to go UP? I'd swear I've seen it somewhere, though it could be a Linux console :)
Forget it. See Far File Manager! It works in Windows console, it works even in PowerShell! How to make something like this? And the coolest part is it restores console state after exiting. So maybe I should ask - how to access console buffer directly? I assume I'll need some native code to do the trick, but maybe there's another way? I thought of clearing console with each update, but this seems like overkill. Or maybe it isn't? Will it blink?
You can move cursor wherever you want: Console.SetCursorPosition or use Console.CursorTop.
Console.SetCursorPosition(0, Console.CursorTop -1);
Console.WriteLine("Over previous line!!!");
Use a carriage return. This sample prints a single line, overwriting what was there before.
Console.WriteLine();
for (int i = 0; i <= 100; i++)
{
System.Threading.Thread.Sleep(10);
Console.Write("\x000DProgress: " + i);
}
This works as long as all your strings are less than 80 columns (or whatever your terminal buffer is set to).
Note: the following answer was originally edited into the question by the OP.
Here's complete solution with demo:
using System;
using System.Collections.Generic;
using System.Threading;
namespace PowerConsole {
internal class Containers {
internal struct Container {
public int Id;
public int X;
public int Y;
public string Content;
}
public static List<Container> Items = new List<Container>();
private static int Identity = 0;
public static int Add(string text) {
var c = new Container();
c.Id = Identity++;
c.X = Console.CursorLeft;
c.Y = Console.CursorTop;
c.Content = text;
Console.Write(text);
Items.Add(c);
return c.Id;
}
public static void Remove(int id) {
Items.RemoveAt(id);
}
public static void Replace(int id, string text) {
int x = Console.CursorLeft, y = Console.CursorTop;
Container c = Items[id];
Console.MoveBufferArea(
c.X + c.Content.Length, c.Y,
Console.BufferWidth - c.X - text.Length, 1,
c.X + text.Length, c.Y
);
Console.CursorLeft = c.X;
Console.CursorTop = c.Y;
Console.Write(text);
c.Content = text;
Console.CursorLeft = x;
Console.CursorTop = y;
}
public static void Clear() {
Items.Clear();
Identity = 0;
}
}
internal class Program {
private static List<Thread> Threads = new List<Thread>();
private static void Main(string[] args) {
Console.WriteLine("So we have some threads:\r\n");
int i, id;
Random r = new Random();
for (i = 0; i < 10; i++) {
Console.Write("Starting thread " + i + "...[");
id = Containers.Add("?");
Console.WriteLine("]");
Thread t = new Thread((object data) => {
Thread.Sleep(r.Next(5000) + 100);
Console.ForegroundColor = ConsoleColor.Green;
Containers.Replace((int)data, "DONE");
Console.ResetColor();
});
Threads.Add(t);
}
Console.WriteLine("\n\"But will it blend?\"...");
Console.ReadKey(true);
i = 0;
Threads.ForEach(t => t.Start(i++));
Threads.ForEach(t => t.Join());
Console.WriteLine("\r\nVoila.");
Console.ReadKey(true);
}
}
}
When building a Windows Console App in C#, is it possible to write to the console without having to extend a current line or go to a new line? For example, if I want to show a percentage representing how close a process is to completion, I'd just like to update the value on the same line as the cursor, and not have to put each percentage on a new line.
Can this be done with a "standard" C# console app?
If you print only "\r" to the console the cursor goes back to the beginning of the current line and then you can rewrite it. This should do the trick:
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
Notice the few spaces after the number to make sure that whatever was there before is erased.
Also notice the use of Write() instead of WriteLine() since you don't want to add an "\n" at the end of the line.
You can use Console.SetCursorPosition to set the position of the cursor and then write at the current position.
Here is an example showing a 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);
}
}
Note that you will have to make sure to overwrite any existing output with new output or blanks.
Update: As it has been criticized that the example moves the cursor only back by one character, I will add this for clarification: Using SetCursorPosition you may set the cursor to any position in the console window.
Console.SetCursorPosition(0, Console.CursorTop);
will set the cursor to the beginning of the current line (or you can use Console.CursorLeft = 0 directly).
So far we have three competing alternatives for how to do this:
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
}
I've always used Console.CursorLeft = 0, a variation on the third option, so I decided to do some tests. Here's the code I used:
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);
}
On my machine, I get the following results:
Backspaces: 25.0 seconds
Carriage Returns: 28.7 seconds
SetCursorPosition: 49.7 seconds
Additionally, SetCursorPosition caused noticeable flicker that I didn't observe with either of the alternatives. So, the moral is to use backspaces or carriage returns when possible, and thanks for teaching me a faster way to do this, SO!
Update: In the comments, Joel suggests that SetCursorPosition is constant with respect to the distance moved while the other methods are linear. Further testing confirms that this is the case, however constant time and slow is still slow. In my tests, writing a long string of backspaces to the console is faster than SetCursorPosition until somewhere around 60 characters. So backspace is faster for replacing portions of the line shorter than 60 characters (or so), and it doesn't flicker, so I'm going to stand by my initial endorsement of \b over \r and SetCursorPosition.
You can use the \b (backspace) escape sequence to backup a particular number of characters on the current line. This just moves the current location, it does not remove the characters.
For example:
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);
}
Here, line is the percentage line to write to the console. The trick is to generate the correct number of \b characters for the previous output.
The advantage of this over the \r approach is that if works even if your percentage output is not at the beginning of the line.
\r is used for these scenarios.
\r represents a carriage return which means the cursor returns to the start of the line.
That's why Windows uses \n\r as its new line marker.
\n moves you down a line, and \r returns you to the start of the line.
I just had to play with the divo's ConsoleSpinner class. Mine is nowhere near as concise, but it just didn't sit well with me that users of that class have to write their own while(true) loop. I'm shooting for an experience more like this:
static void Main(string[] args)
{
Console.Write("Working....");
ConsoleSpinner spin = new ConsoleSpinner();
spin.Start();
// Do some work...
spin.Stop();
}
And I realized it with the code below. Since I don't want my Start() method to block, I don't want the user to have to worry about writing a while(spinFlag) -like loop, and I want to allow multiple spinners at the same time I had to spawn a separate thread to handle the spinning. And that means the code has to be a lot more complicated.
Also, I haven't done that much multi-threading so it's possible (likely even) that I've left a subtle bug or three in there. But it seems to work pretty well so far:
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();
}
}
Explicitly using a Carrage Return (\r) at the beginning of the line rather than (implicitly or explicitly) using a New Line (\n) at the end should get what you want. For example:
void demoPercentDone() {
for(int i = 0; i < 100; i++) {
System.Console.Write( "\rProcessing {0}%...", i );
System.Threading.Thread.Sleep( 1000 );
}
System.Console.WriteLine();
}
public void Update(string data)
{
Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
Console.Write(string.Format("\r{0}", data));
}
From the Console docs in MSDN:
You can solve this problem by setting
the TextWriter.NewLine property of the
Out or Error property to another line
termination string. For example, the
C# statement, Console.Error.NewLine =
"\r\n\r\n";, sets the line termination
string for the standard error output
stream to two carriage return and line
feed sequences. Then you can
explicitly call the WriteLine method
of the error output stream object, as
in the C# statement,
Console.Error.WriteLine();
So - I did this:
Console.Out.Newline = String.Empty;
Then I am able to control the output myself;
Console.WriteLine("Starting item 1:");
Item1();
Console.WriteLine("OK.\nStarting Item2:");
Another way of getting there.
This works if you want to make generating files look cool .
int num = 1;
var spin = new ConsoleSpinner();
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("");
while (true)
{
spin.Turn();
Console.Write("\r{0} Generating Files ", num);
num++;
}
And this is the method that i got from some answer below and modified it
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);
}
}
Here's another one :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]);
}
}
}
If you want update one line, but the information is too long to show on one line, it may need some new lines. I've encountered this problem, and below is one way to solve this.
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");
}
}
i was looking for same solution in vb.net and i found this one and it's great.
however as #JohnOdom suggested a better way to handle the blanks space if previous one is larger than current one..
i make a function in vb.net and thought someone could get helped ..
here is my 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
I was doing a search for this to see if the solution I wrote could be optimised for speed. What I wanted was a countdown timer, not just updating the current line.
Here's what I came up with. Might be useful to someone
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("");
Inspired by #E.Lahu Solution, implementation of a bar progress with percentage.
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);
}
}
Here is my take on s soosh's and 0xA3's answers.
It can update the console with user messages while updating the spinner and has an elapsed time indicator aswell.
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();
}
}
usage is something like this:
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.");
}
}
The SetCursorPosition method works in multi-threading scenario, where the other two methods don't