Console ReadLine Intercept App

A .NET Class Enhancing Console.ReadLine(Intercept) Inputs.

Console.ReadLine(true) enables the class presented in this article to intercept keys —including cursor, function, and modifier keys— making menu coding and input management easier.

Introduction

The main reason of this App is to provide a programming tool, when calling the shared method Cnsl.ReadLine(), so that user pressing key functions or, otherwise, entering a string can be easily managed. Thus, clearly distinguishing a valid or to-be-validated input from a function input. Functions F1, F2, ... are completely configurable -i.e. custom function keys, so you may use more or less custom function keys accordingly to your needs.

Sample Image

This allows the normal flow of the program to be modified. In the present app, for example, pressing F3 at any of the program's inputs returns the program to the beginning at the first entry, or pressing F4 at any input returns the flow from any subroutine up to the first level (Sub Main()) and terminates.

Program Flow

Initialization: The program starts by displaying a help message and then enters a loop where the user can interact with it through a series of questions.

Flowchart

(1) In the flow chart, if returning from Examples(), line contains a Fn then the flow continues at (*). Thus, F1?, F2?, F3?, F4? are inside a loop exited when it is not F3 (calling Examples() again).

  • Main Input Menu (Menu function): This is the main part of the program where the user is prompted to enter:
    • CultureInfo: The culture setting that defines number formatting (for example, en-US or es-ES).
    • Number of Decimal Places: A number between 0 and 15.
    • Unit Value: A numerical value that will be converted to engineering notation.
    • Unit Name: The name of the unit (e.g., "V" for volts, "A" for amperes).
  • If all inputs are valid, then takes place the Engineering Notation Conversion: The function ToEngineeringNotation converts the value provided by the user into engineering notation. It adjusts the value using metric prefixes to make the number easier to read (e.g., converting 12345 volts to 12.345 kV).
  • Handling Keys:
    • F1: Displays help information.
    • F2: Allows the user to restart the input process.
    • F3: Provides examples of input values.
    • F4: Clears the console screen.
    • F5: Exits the program.
    • Esc: Clears the current input.
    • Key up: Recovers a previous input (if any).
    • Key down: Recovers a next input (if any).
    • Key left: moves cursor to previous input character (if any)
    • Key right: moves cursor to next input character (if any)
    • Key delete: delete the character ahead of the cursor (if any)
    • Key backspace: moves the display cursor one position backwards and deletes the character at that position (if any)
  • There are three types of messages.
    • DspHelp. Displays help information, detailing the actions of each function key.
    • Display Eng. Notation. Calls the conversion and, once converted, displays the engineering notation.
    • DspErrMsg. Displays an error message for non valid inputs..

Function Menu is as follows:

A screenshot of the user interface during input handling in Examples()

Screenshot of the user interface during input handling

If the selected option inside line is numeric and valid the flow will continue 'down', otherwise it will display an error message and repeat the loop asking again for the same question.

In the above image, as in all the others inputs in this App, behind the scene, the code is calling ReadLine() it passes a string to display (before the input takes place). Let's see all the arguments.

            Public Shared Function ReadLine(strToWrite As String,
                                        intercept As Boolean,
                                        Optional saveInput As Boolean = True,
                                        Optional defaultValue As String = "",
                                        Optional minLength As Int32 = 1) As String

                If Not intercept Then Return Console.ReadLine
                .....
            End Function
                    
  • strToWrite is the string to display before the input. (If ended with vbCrLf the input will be in the next line)
  • intercept: if false a 'common' Console.ReadLine() instruction will be executed and returned its value.
  • saveInput: save (if true) the user input, so that in next calls to this method, user can recover previous inputs pressing key up and key down, just like as the Command Window does.
  • defaultValue: a string with the default value. The user may accept (pressing Enter), edit the defaultValue or enter any other value
  • minLength: the function will not end if the input string length is less than minLength (or a funcition key F1, F2, ... is pressed)

We may define the allowed function keys depending on our needs. This is done in a JSON file:

                          { // FUNCTION KEYS
                                "DspHelp": "112",
                                "AskAgain": "113",
                                "Examples": "114",
                                "CLS": "115",
                                "Quit": " 116",
                          }
                    
                    

These pair values are read in class CnslFns and stored in a Dictionary(Of String, String):

            Public Class CnslFns
                    Shared config As String = _
                    IO.File.ReadAllText("FunctionKeys.json")
                    Public Shared functionKeys As Dictionary(Of String, String) = _
                    JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(config)
                    Public Shared Function IsNotAFunctKey(s As String) As Boolean
                        Return s.Length = 0 OrElse AscW(s) <> 27
                    End Function
            End Class
  
                    

Adding a new function

If you add a new function Fx you just will have to add it to the JSON file. For example, to capture Shift+Control+Alt+A as a new custom function:

            { // FUNCTION KEYS
                "DspHelp": "112",
                "AskAgain": "113",
                "Examples": "114",
                "CLS": "115",
                "Quit": " 116",
                "MyCustomFn": "065SCA" 
                // The first character, always code 27 for ESC,
                // Additionally, for MyCustomFn and as example added SCA modifiers: 
                // 065=A; and S for shift key, C for Control, A for Alt modifiers
                }
                    

Note

Although it is not strictly necessary the first character  (code 27) in for ex. "DspHelp": "112" it is advisable to do it so, because if the user presses Esc (code 27) the excecution will stay inside ReadLine(), and it will be guaranteed that a valid input will never match one of those constants.

Conclusion and Points of Interest

Until recently I was unaware of the ReadLine(intercept) method, but I recognize that it is very useful.

I suggest the reader to try adding the Cnsl.vb file to your new Console project and include a menu like the one presented, modifying as necessary.

For JSON to work in a new VStudio2022 App, go to menu options path: Tools/NuGet packages management/Solution Packages Administration and install NuGet package Newtonsoft.Json.

Source download here.

 

Addendum. Enhancements.

  1. Validation:
    Added CnslValidation class to handle different input types (e.g., Integer, Double) with customizable validation logic.
  2. Callback Functionality:
    Introduced a delegate CnslCallback allowing for a callback function to be invoked when input is validated, enabling additional processing.
  3. Error Messages & Patterns:
    Enhanced validation includes error messages and optional regex patterns for more specific input control.
  4. Culture Support:
    Added optional CultureInfo for parsing numeric input.

The refactored Cnsl.ReadLine method is:

        Public Shared Function ReadLine(strToWrite As String,
            intercept As Boolean,
            Optional saveInput As Boolean = True,
            Optional defaultValue As String = "",
            Optional minLength As Int32 = 1,
            Optional validation As CnslValidation = Nothing) As String
Where CnslValidation constructor signature is:
    Public Sub New(validationType As CnslValTypes,
                   displayErrMsg As Boolean,
                   Optional minLength As Int32 = 1,
                   Optional maxNumOfTries As Int32 = 25,
                   Optional regexPattern As String = "",
                   Optional cullture As Globalization.CultureInfo = Nothing,
                   Optional callback As CnslCallback = Nothing)

and the delegate is defined:
Public Delegate Function CnslCallback(ByRef input As StringBuilder, objVal As CnslValidation) As Boolean

To download the latest example featuring enhanced validation and callback handling directly, click here