I have a C# console program that asks the user for a certain word by showing a input prompt. The input is then processed using an switch statement. If the word is correct, the program continues. But if the input doesn't match, the program says "Error: invalid input" and then goes back to the input prompt. The tricky part is that I want the program to clear the input that the user just typed before pressing Enter and prompt the user again, without making another separate prompt below the first one.
Is there a library of some kind in C# that does something like that, or do I have to make a library for it?
One way to do this is to use a combination of Console.SetCursorPosition and Console.Write to set the cursor to the beginning of their response, write enough whitespace to "erase" their reaponse, and then set the cursor back to the beginning again.
For example:
static string GetUserInput(string prompt, List<string> validResponses)
{
Console.Write(prompt);
// Capture the cursor position just after the prompt
var inputCursorLeft = Console.CursorLeft;
var inputCursorTop = Console.CursorTop;
// Now get user input
string input = Console.ReadLine();
while (validResponses != null &&
validResponses.Any() &&
!validResponses.Contains(input, StringComparer.OrdinalIgnoreCase))
{
Console.ForegroundColor = ConsoleColor.Red;
// PadRight ensures that this line extends the width
// of the console window so it erases itself each time
Console.Write($"Error! '{input}' is not a valid response".PadRight(Console.WindowWidth));
Console.ResetColor();
// Set cursor position to just after the promt again, write
// a blank line, and reset the cursor one more time
Console.SetCursorPosition(inputCursorLeft, inputCursorTop);
Console.Write(new string(' ', input.Length));
Console.SetCursorPosition(inputCursorLeft, inputCursorTop);
input = Console.ReadLine();
}
// Erase the last error message (if there was one)
Console.Write(new string(' ', Console.WindowWidth));
return input;
}
In use this might look like:
static void Main(string[] args)
{
var validResponses = new List<string> {"Yes", "No"};
var userInput = GetUserInput("Do you like me? ", validResponses);
if (userInput.Equals("Yes", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("I like you too!");
}
else
{
Console.WriteLine("And all along I thought you had good taste.");
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Here's a sample run of the program. I had to include several screenshots since the response (and then the error message) are cleared on each iteration:
Try this ...
static void Main(string[] args)
{
CheckWord();
Console.ReadKey();
}
private static void CheckWord()
{
while (true)
{
string errorMessage = "Error: invalid input ... enter a valid entry";
string word = Console.ReadLine();
if (word != "word")
{
Console.SetCursorPosition(word.Length +1 , (Console.CursorTop) - 1);
Console.Write(errorMessage);
Console.ReadKey();
Console.SetCursorPosition(0, Console.CursorTop);
for (int i = 0; i <= word.Length + errorMessage.Length +1 ; i++)
{
Console.Write(" ");
}
Console.SetCursorPosition(0, Console.CursorTop);
}
else
{
break;
}
}
}
Related
I'm building a webshop mockup in a console app (school project) where the user registers with a number of readlines. For example; the user is prompted to input their desired age, but if the age is lower than 15 or higher than 100 or if the input isn't parseable as an int, the user gets a .Write("Error, try again: ").
My goal is to remain on the same line until the user inputs an age that is valid.
Error, try again: _______
right now it just keeps posting the same error message making the whole thing unreadable.
public static void ClearLine()
{
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition(0, Console.CursorTop - (Console.WindowWidth >= Console.BufferWidth ? 1 : 0));
}
With this code I am able to remain on the same line, but the error message disappears & the text i just wrote still remains and the console simply writes over it, it's better for sure but I'd love it if I could just clear the text and keep inputting until it's valid
if (!int.TryParse(Console.ReadLine(), out number) ||
number > maxValue || number < minValue)
{
Console.Write("Wrong input, try again: ");
Thread.Sleep(800);
ClearLine();
}
and this is the code that tries the input
It seems you're writing a Console app, not a Web app (webshop)
Your teacher must be over 60 to put this requirement. In the old days, we did it like this on a VT-330 terminal. Input only backspace, ENTER and numbers..
string squestion = "What's your age ?";
int lenquestion = squestion.Length + 1;
while (true) {
string sanswer = "";
Console.Write(squestion + " ___");
Console.SetCursorPosition(lenquestion, Console.CursorTop);
while (true) {
var c = Console.ReadKey();
if (c.Key == ConsoleKey.Enter) break;
if (c.Key != ConsoleKey.Backspace) sanswer += c.KeyChar;
else if (sanswer != "") sanswer = sanswer.Remove(sanswer.Length - 1);
Console.SetCursorPosition(lenquestion, Console.CursorTop);
Console.Write(sanswer + '_');
Console.SetCursorPosition(lenquestion+sanswer.Length, Console.CursorTop);
}
Console.Write(squestion+" "+sanswer);
if (int.TryParse(sanswer, out int age)) {
if (age < 60 || age > 120) {
Console.WriteLine("\t ... you lied ! try again.."); // same line
continue;
}
}
else {
Console.WriteLine("\t ... invalid input ! try again.."); // same line
continue;
}
Console.WriteLine("\t ... thank you for being honest !");
Console.WriteLine("\nPress <enter> to leave..");
Console.ReadLine();
break;
}
I am trying to make a small typing racer program where the user will type the words displayed above. I want the program to tell the user their time when the user has met the character limit. I'm able to do this however the user has to press enter after they have finished the sentence. I want to make it so program simultaneously tracks the length of the sentence. I believe that this is something do with thread managing.
Code
var words = "the you that it he she why when is";
TimeOnly timeNow1 = new TimeOnly();
while (words2.Length !< numwords)
{
timeNow1 = TimeOnly.FromDateTime(DateTime.Now);
Console.WriteLine(words2.Length);
words2 = Console.ReadLine();
}
var timeNow2 = TimeOnly.FromDateTime(DateTime.Now);
var time = (timeNow2 - timeNow1);
Console.WriteLine(time.Seconds);
Console.WriteLine(words2.Length);
You will need to read individual keys with Console.ReadKey instead of whole lines with Console.ReadLine if you don't want to press enter, as a line requires a newline character to be completed. That will in return require you to handle string concatenation manually and deal with cases like the backspace key.
You will also have to do a little bit of manual cursor manipulation if you want to continously output your input (or its length).
I've omitted your time tracking as it did not seem relevant to your question and made a rough example:
string words = "the you that it he she why when is";
int maxChar = words.Length;
Console.WriteLine(words);
Console.WriteLine($"Max characters: {maxChar}");
string input = "";
while (input.Length! < maxChar)
{
Console.SetCursorPosition(0, 4);
var keyInfo = Console.ReadKey();
//handle backspace and any other char you want to omit from input here
if (keyInfo.Key == ConsoleKey.Backspace)
input = input.Substring(0, Math.Max(0, input.Length - 1));
else
input += keyInfo.KeyChar;
Console.SetCursorPosition(0, 2);
Console.WriteLine($"You wrote: \"{ input }\"".PadRight(Console.WindowWidth));
Console.SetCursorPosition(0, 3);
Console.WriteLine($"Number of chars: {input.Length}".PadRight(Console.WindowWidth));
}
//set cursor below previous output
Console.SetCursorPosition(0, 5);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
PadRight(Console.WindowWidth) prevents previous WriteLine calls with longer strings to mess up your output.
There are definitely some optimizations left to be done to that code but that should get you started.
Your question is "How can I output the length of string as I type it?". Here's a primitive implementation that continuously displays the length of the characters typed in the Title bar, and also uses a Stopwatch to display the current rate in characters per minute..
class Program
{
static void Main(string[] args)
{
var stopwatch = new System.Diagnostics.Stopwatch();
var words = "the you that it he she why when is";
var builder = new StringBuilder();
Console.Title = "Type Racer";
Console.WriteLine($"Please type:");
Console.WriteLine(words);
while(!builder.ToString().Equals(words))
{
var keyInfo = Console.ReadKey();
if(builder.Length.Equals(0))
{
stopwatch.Start();
}
switch (keyInfo.Key)
{
case ConsoleKey.Backspace:
builder.Remove(builder.Length - 1, 1);
Console.Write(' ');
Console.Write('\b');
break;
default:
builder.Append(keyInfo.KeyChar);
break;
}
var seconds = stopwatch.Elapsed.Ticks / (double)TimeSpan.TicksPerSecond;
var charactersPerSecond = builder.Length / seconds;
Console.Title = $"{builder.Length} characters in {seconds} seconds = {(charactersPerSecond * 60).ToString("F1")} CPM";
}
Console.WriteLine();
Console.WriteLine("Match!");
Console.ReadKey();
}
}
The user enters a numbers and the program should make a sideways graph by writing "-" * the amount of digits in the number, but it writes the "-" a line under the user input
Current output:
Expected output:
static void Main(string[] args)
{
int num1, num2;
Console.WriteLine("how many numbers will you want to enter?");
num1 = int.Parse(Console.ReadLine());
Console.WriteLine("Enter " + num1 + " numbers");
for(; num1 > 0; num1--)
{
num2 = int.Parse(Console.ReadLine());
Hi(num2);
}
}
static void Hi(int num)
{
while(num != 0)
{
num /= 10;
Console.Write("-");
}
Console.WriteLine()
}
You can get and set the cursor position in the console, so if you remember which line it is on before the user presses enter for the number entry, you can put the cursor back on that line.
Also, to print a number of dashes of the length of the input, it is not necessary for the input to be digits (or you would have checked for that).
Something like this should be suitable:
static void Main(string[] args)
{
Console.Write("How many numbers will you want to enter? ");
int num1 = int.Parse(Console.ReadLine());
Console.WriteLine("Enter " + num1 + " numbers");
for (; num1 > 0; num1--)
{
int currentLine = Console.CursorTop;
string num2 = Console.ReadLine();
Console.SetCursorPosition(20, currentLine);
Console.WriteLine(new string('-', num2.Length));
}
Console.WriteLine("\r\n(Press enter to leave program.)");
Console.ReadLine();
}
Sample output:
How many numbers will you want to enter? 4
Enter 4 numbers
1 -
435 ---
What happens long wi-----------------------
(Press enter to leave program.)
Use a method like the following:
public string getKeyBuffer()
{
string buffer = "";
do
{
var charIn = Console.ReadKey(true);
if (charIn.Key == ConsoleKey.Enter) break;
buffer += charIn.KeyChar;
Console.Write(charIn.KeyChar);
} while (true);
return buffer;
}
This will echo each key pressed and then return all the keys pressed once the user presses the enter key without echoing the enter key.
The best solution would be to write to something other than the console, where you would have absolute control over what is displayed and where.
Another solution would be to format a string in your code, then clear the console and write the entire thing each time.
Another solution would be to keep track of where you are and move the console cursor using Console.SetCursorPosition. However, this is rarely a satisfying solution given the existence of nicer output alternatives.
You can move the cursor up one line with Console.CursorTop--;, avoiding the necessity of keeping track of which line you are on.
When the input's type is not an integer, the program should fail. However, there are two problems:
After a letter is typed and I get the "not valid" response, then if the next input is a number, it won't accept it and says "not valid".
How can I make it so when a wrong number is inputted, it stays on the same row and just clears the previous input from the screen (and allows for a retry from the same position)?
static void Main(string[] args)
{
int firstNum;
int Operation = 1;
switch (Operation)
{
case 1:
Console.SetCursorPosition(0, 0);
Console.Write("Write a number: ");
firstNum = ReadInteger("");
Console.ReadKey();
break;
}
}
private static int ReadInteger(string title)
{
while (true)
{
if (!string.IsNullOrWhiteSpace(title))
Console.WriteLine(title);
string input = Console.ReadLine();
if (int.TryParse(input, out int result))
return result;
Console.WriteLine("Sorry, not a valid integer value; please, try again.");
Console.ReadKey();
}
}
Ad 1)
Because you have Console.ReadKey at the end of ReadInteger which will be executed as well. So if you hit Console.ReadLine at the line string input = Console.ReadLine(); and enter something that is not a number, int.TryParse will return false. This leads to the error message and the execution of Console.ReadKey. So you should first of all get rid of that Console.ReadKey there.
Ad 2)
You are setting the cursor position before the call of ReadInteger but not within ReadInteger. So if someone enters a text, the end of the input is typically done by pressing enter. You then write a line (with Console.WriteLine). So if you want to have the cursor at the same position, you will have to reset it's position within the loop which is within the ReadInteger method.
How about something like this:
public static int ReadInt(string prompt)
{
Console.Clear();
var length = prompt.Length + 2; //2 is for a colon and a space
var position = length;
Console.Write($"{prompt}: ");
string buffer = String.Empty;
int returnNum = 0;
while (true)
{
Console.SetCursorPosition(position, 0);
var charRead = Console.ReadKey();
if(charRead.KeyChar == '\r')
{
return returnNum;
}
if (!int.TryParse(buffer + charRead.KeyChar, out returnNum))
{
Console.SetCursorPosition(position, 0);
Console.WriteLine(" "); //overwrite
Console.SetCursorPosition(0, 1);
Console.Write("Error: enter only digits");
continue;
}
else
{
buffer += charRead.KeyChar;
++position;
//overwrite any error
Console.SetCursorPosition(0, 1);
Console.Write(" ");
}
}
}
It's not perfect. It doesn't handle typing in too many digits. It doesn't handle backspace. It clears the console to establish position (it doesn't look like you can read the console position, and I'm too lazy to keep track of things).
If you do handle backspace, make sure you don't let users backup too much. It's been years (um, no, decades) since I've done low level console management, but I can remember doing this in CP/M in the early 80s - if you backed up to far, the OS died (which would be a very result in Windows :-) ).
Im working with ConsoleKeyInfo in C# but i have problems with Console.ReadKey when I try to write numbers greater than 9 in the console, for example
ConsoleKeyInfo number;
Console.Write("Write a number: ");
number = Console.ReadKey();
If i want to write 10 or 11... the console only reads the "1"
I dont wanna use Console.ReadLine because I don want to press "Enter" for each number.
Is there another way to use Console.ReadKey to wait maybe 1 second before continue?
Thanks
As the comments on the question say, Console.ReadKey only reads a single key by definition. You will need to use a different function if you want to get more input from the console. Try something like this, for instance:
Console.Write("Write a number: ");
string line = Console.ReadLine();
int num = 0;
if (line != null)
num = int.Parse(line);
That's a start, with minimal error checking. See what you can get from there.
The best you can do is use Console.ReadLine(). There's no way the program will know you have finished the number.
UPDATE
If you have a fixed length number (i.e. a 13-digit ISBN), you can use ReadKey, but like this:
string isbn = "";
while (isbn.Length < 13)
{
isbn += Console.ReadKey().KeyChar;
}
The idea is that you have to call cki = Console.ReadKey(true) multiple times.
ConsoleKeyInfo cki;
string userNumber = "";
while(true)
{
System.Console.WriteLine("Press more than one key:");
cki = Console.ReadKey(true);
if(cki.Key == ConsoleKey.Escape)
{
System.Console.WriteLine("GOOD BYE!");
Environment.Exit(0);
}
else
{
if(char.IsNumber(cki.KeyChar) || cki.Key == ConsoleKey.Enter)
{
while(char.IsNumber(cki.KeyChar))
{
Console.Write(cki.KeyChar);
userNumber += (cki.KeyChar).ToString();
cki = Console.ReadKey(true); // !!! Main idea
}
System.Console.WriteLine();
System.Console.WriteLine($"Your number is: {userNumber}");
Environment.Exit(0);
}
else
{
System.Console.WriteLine("Wrong symbol! Input a number!");
}
}
}