简体   繁体   中英

How to fix Console output when Console.ReadKey receives an Escape key in C#?

I want to read user inputs from a C# console application targeting .NET Core 3.1. In some situations, the user shall have the choice to cancel the input and the related operation by pressing the Escape key at any point of time when the user is prompted for input.

The following script works for empty strings (user just hit Enter and nothing else), and normal user input (user enters characters and confirms with Enter ).

But there is a issue: When the user cancels the input (hits Escape ), the first character of the following Console.WriteLine is suppressed, and Console.Write outputs nothing. Observations shows, that an line break must be outputted to restore Console.WriteLine and Console.Write expected behaviour.

Another issue directly related to this is that when I inject an additional line break, in the case of a normal input, I receive 2 line breaks, and in case of a canceled input, I receive 1 line break.

using System;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApp1
{
    internal class Program
    {
        internal const ConsoleColor DefaultConsoleForegroundColor = ConsoleColor.Gray;
        internal const ConsoleColor DefaultConsoleInputForegroundColor = ConsoleColor.White;

        internal static readonly Regex WhitespaceRegex = new Regex(@"\s{2,}", RegexOptions.Compiled | RegexOptions.CultureInvariant);

        internal static readonly Encoding UTF8NoBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false);

        private static void Main()
        {
            Console.InputEncoding = UTF8NoBomEncoding;
            Console.OutputEncoding = UTF8NoBomEncoding;
            Console.Title = @"ConsoleApp1";
            Console.TreatControlCAsInput = true;
            Console.Clear();
            Console.ResetColor();
            Console.ForegroundColor = DefaultConsoleForegroundColor;

            string firstName = ConsoleReadLine(message: @"Enter your first name: ", treatEmptyOrWhitespaceAsNull: true) ?? @"World";
            Console.WriteLine(@"Hello, {0}!", firstName);

            ConsoleReadKey(message: @"Press any key to exit...", hideInput: true, messageColor: DefaultConsoleInputForegroundColor);
        }

        internal static string ConsoleReadLine(string message = null, bool hideInput = false, bool treatEscapeAsCancel = true,
                                               bool trimInput = true, bool normalizeWhitespace = true,
                                               bool treatEmptyOrWhitespaceAsNull = false, bool addLineBreak = true,
                                               ConsoleColor messageColor = DefaultConsoleForegroundColor,
                                               ConsoleColor inputColor = DefaultConsoleInputForegroundColor)
        {
            string inputString;

            // Print optional message before the prompt (same line).
            Console.ForegroundColor = messageColor;
            if (!string.IsNullOrWhiteSpace(message))
                Console.Write(message);

            // Get input from user.
            Console.ForegroundColor = inputColor;
            StringBuilder inputBuilder = new StringBuilder();
            while (true)
            {
                ConsoleKeyInfo inputKey = Console.ReadKey(hideInput);
                if (inputKey.Key == ConsoleKey.Enter)
                {
                    inputString = inputBuilder.ToString();
                    break;
                }
                else if (treatEscapeAsCancel && inputKey.Key == ConsoleKey.Escape)
                {
                    inputString = null;
                    break;
                }
                else
                    inputBuilder.Append(inputKey.KeyChar);
            }

            // Optional input modifications.
            if (inputString != null)
            {
                if (trimInput)
                    inputString = inputString.Trim();
                if (normalizeWhitespace)
                    WhitespaceRegex.Replace(inputString, @" ");
                if (treatEmptyOrWhitespaceAsNull && string.IsNullOrWhiteSpace(inputString))
                    inputString = null;
            }

            // Reset foreground color.
            Console.ForegroundColor = DefaultConsoleForegroundColor;

            if (addLineBreak)
                Console.WriteLine();

            return inputString;
        }

        internal static ConsoleKeyInfo ConsoleReadKey(string message = null, bool hideInput = false, bool addLineBreak = true,
                                                      ConsoleColor messageColor = DefaultConsoleForegroundColor,
                                                      ConsoleColor inputColor = DefaultConsoleInputForegroundColor)
        {
            ConsoleKeyInfo key;

            // Print optional message before the prompt (same line).
            Console.ForegroundColor = messageColor;
            if (!string.IsNullOrWhiteSpace(message))
                Console.Write(message);

            // Get input from user.
            Console.ForegroundColor = inputColor;
            key = Console.ReadKey(hideInput);

            // Reset foreground color.
            Console.ForegroundColor = DefaultConsoleForegroundColor;

            if (addLineBreak)
                Console.WriteLine();

            return key;
        }
    }
}

Back a couple of ages ("the old days"), I had to double/triple read the keyboard input due to the control character was also submitted. However, there is nothing in the input buffer after the Escape key was registered.

How to fix the input cancellation issues related to pressing the Escape key?


Example 1 ( correct )

Enter your first name: Test<Enter>
Hello, Test!
Press any key to exit...

Example 2 ( wrong )

Enter your first name: Test<Escape>
ello, World!
Press any key to exit...

Example 3 ( correct )

Enter your first name: <Enter>
Hello, World!
Press any key to exit...

Example 4 ( wrong )

Enter your first name: <Escape>
ello, World!
Press any key to exit...

Thanks to @jscarle, the solution is to not allow Console.ReadKey to print characters, but do it manually.

Here is the code, that fixes the ConsoleReadLine function.

while (true)
{
    ConsoleKeyInfo inputKey = Console.ReadKey(true);
    if (inputKey.Key == ConsoleKey.Enter)
    {
        inputString = inputBuilder.ToString();
        break;
    }
    else if (treatEscapeAsCancel && inputKey.Key == ConsoleKey.Escape)
    {
        inputString = null;
        break;
    }
    else
        inputBuilder.Append(inputKey.KeyChar);

    if (!hideInput)
        Console.Write(inputKey.KeyChar);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM