20

How can you set a default input value in a .net console app?

Here is some make-believe code:

Console.Write("Enter weekly cost: ");
string input = Console.ReadLine("135"); // 135 is the default. The user can change or press enter to accept
decimal weeklyCost = decimal.Parse(input);

Of course, I don't expect it to be this simple. I am betting on having to do some low-level, unmanaged stuff; I just don't know how.

EDIT

I know I can replace no input with the default. That's not what I am asking about. I am trying to LEARN what's involved in achieving the behavior I described: giving the user an editable default. I'm also not worried about input validation; my question has nothing to do with that.

Community
  • 1
  • 1
Ronnie Overby
  • 45,287
  • 73
  • 267
  • 346
  • You can code this as the answer suggested - the user will not care about the coding technic. For the theoretical question if there is a way to do it with readline - probably not (at least not documented). – Dani Oct 31 '09 at 19:29
  • But - I see were you're trying to go, we are looking for a solution that will enable the user to change the default text. – Dani Oct 31 '09 at 19:32
  • I know it can't be done with .ReadLine(). But, I know there is a way to do this. – Ronnie Overby Oct 31 '09 at 19:34

8 Answers8

11

I believe that you will have manage this manually by listening to each key press:

Quickly thown together example:

   // write the initial buffer
   char[] buffer = "Initial text".ToCharArray();
   Console.WriteLine(buffer);

   // ensure the cursor starts off on the line of the text by moving it up one line
   Console.SetCursorPosition(Console.CursorLeft + buffer.Length, Console.CursorTop - 1);

   // process the key presses in a loop until the user presses enter
   // (this might need to be a bit more sophisticated - what about escape?)
   ConsoleKeyInfo keyInfo = Console.ReadKey(true);
   while (keyInfo.Key != ConsoleKey.Enter)
   {

       switch (keyInfo.Key)
       {
            case ConsoleKey.LeftArrow:
                    ...
              // process the left key by moving the cursor position
              // need to keep track of the position in the buffer

         // if the user presses another key then update the text in our buffer
         // and draw the character on the screen

         // there are lots of cases that would need to be processed (backspace, delete etc)
       }
       keyInfo = Console.ReadKey(true);
   }

This is quite involved - you'll have to keep ensure the cursor doesn't go out of range and manually update your buffer.

Matt Breckon
  • 3,374
  • 20
  • 26
  • I don't think this is what is meant by the question. – driis Oct 31 '09 at 19:54
  • 4
    Actually this is definitely the best answer so far. – Ronnie Overby Oct 31 '09 at 19:56
  • Throw this into an Extension method so you could call Console.ReadLine("135"); Can Extension methods be overloads of existing methods? If not, give it a new name. – CoderDennis Oct 31 '09 at 20:00
  • 1
    @Dennis - You can't add extension methods to static classes because you can't create instances of them. Maybe SuperConsole.ReadLine("default")? There are a lot of cases to handle, but coding this out and packaging it into a DLL should be worth it. – Ronnie Overby Oct 31 '09 at 20:18
  • I got "static types cannot be used as parameters" when I tried to create an extension method for Console. So maybe that wouldn't work & maybe that's a new SO question. – CoderDennis Oct 31 '09 at 20:21
9

Here's a simple solution:

public static string ConsoleReadLineWithDefault(string defaultValue)
{
    System.Windows.Forms.SendKeys.SendWait(defaultValue);
    return Console.ReadLine();
}

It's not complete however. Some characters in the SendWait input string have special meaning so you have to escape them (eg. +, (, ), etc.) See: http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx for a complete description.

Vizu
  • 1,871
  • 1
  • 15
  • 21
7

I went ahead and completed Matt's implementation approach:

    public static string ReadInputWithDefault(string defaultValue, string caret = "> ")
    {
        Console.WriteLine(); // make sure we're on a fresh line

        List<char> buffer = defaultValue.ToCharArray().Take(Console.WindowWidth - caret.Length - 1).ToList();
        Console.Write(caret); 
        Console.Write(buffer.ToArray());
        Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop);

        ConsoleKeyInfo keyInfo = Console.ReadKey(true);
        while (keyInfo.Key != ConsoleKey.Enter)
        {
            switch (keyInfo.Key)
            {
                case ConsoleKey.LeftArrow:
                    Console.SetCursorPosition(Math.Max(Console.CursorLeft - 1, caret.Length), Console.CursorTop);
                    break;
                case ConsoleKey.RightArrow:
                    Console.SetCursorPosition(Math.Min(Console.CursorLeft + 1, caret.Length + buffer.Count), Console.CursorTop);
                    break;
                case ConsoleKey.Home:
                    Console.SetCursorPosition(caret.Length, Console.CursorTop);
                    break;
                case ConsoleKey.End:
                    Console.SetCursorPosition(caret.Length + buffer.Count, Console.CursorTop);
                    break;
                case ConsoleKey.Backspace:
                    if (Console.CursorLeft <= caret.Length)
                    {
                        break;
                    }
                    var cursorColumnAfterBackspace = Math.Max(Console.CursorLeft - 1, caret.Length);
                    buffer.RemoveAt(Console.CursorLeft - caret.Length - 1);
                    RewriteLine(caret, buffer);
                    Console.SetCursorPosition(cursorColumnAfterBackspace, Console.CursorTop);
                    break;
                case ConsoleKey.Delete:
                    if (Console.CursorLeft >= caret.Length + buffer.Count)
                    {
                        break;
                    }
                    var cursorColumnAfterDelete = Console.CursorLeft;
                    buffer.RemoveAt(Console.CursorLeft - caret.Length);
                    RewriteLine(caret, buffer);
                    Console.SetCursorPosition(cursorColumnAfterDelete, Console.CursorTop);
                    break;
                default:
                    var character = keyInfo.KeyChar;
                    if (character < 32) // not a printable chars
                        break;
                    var cursorAfterNewChar = Console.CursorLeft + 1;
                    if (cursorAfterNewChar > Console.WindowWidth || caret.Length + buffer.Count >= Console.WindowWidth - 1)
                    {
                        break; // currently only one line of input is supported
                    }
                    buffer.Insert(Console.CursorLeft - caret.Length, character);
                    RewriteLine(caret, buffer);
                    Console.SetCursorPosition(cursorAfterNewChar, Console.CursorTop);
                    break;
            }
            keyInfo = Console.ReadKey(true);
        }
        Console.Write(Environment.NewLine);

        return new string(buffer.ToArray());
    }

    private static void RewriteLine(string caret, List<char> buffer)
    {
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(new string(' ', Console.WindowWidth - 1));
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(caret);
        Console.Write(buffer.ToArray());
    }

Notes:

  • Works for only one line of input
  • You can define what stands before the editable text area (caret parameter)
  • Use at your own risk, there may still be some IndexOutOfBound-problems. ;)
Efrain
  • 3,248
  • 4
  • 34
  • 61
5

Or... Just test the value entered, if it's empty put the default value in input.

Dani
  • 14,639
  • 11
  • 62
  • 110
4

There's a much better way to do this now, check out Readline on nuget: https://www.nuget.org/packages/ReadLine

  1. install-package Readline
  2. var input = ReadLine.Read("Enter weekly cost: ", "135");

I like to use the console to write interactive tests, and having default values can really help things.

Lunster
  • 896
  • 6
  • 17
  • 4
    Slight difference here: the @default is just a default value, not an automatically inserted value that can be erased by the user – Peder Rice Mar 14 '19 at 19:20
  • a good framework to use... but only .net standard 2.0... not available for .net framework... with .net framework I get an error on trying install that package – Marcus.D Apr 04 '19 at 13:57
  • Sorry. I was wrong with my statement. I find out, I need at least .NET Framework 4.6.1 but in my project I used 4.5.2. That's the reason, why I couldn't use that library/nuget-package. – Marcus.D Apr 09 '19 at 10:13
3
  1. Add Reference to Assembly Library "System.Windows.Forms" to your Project
  2. Add SendKeys.SendWait("DefaultText") immediately after your Console.WriteLine command and before your Console.ReadLine command

 

string _weeklycost = "";
Console.WriteLine("Enter weekly cost: ");
System.Windows.Forms.SendKeys.SendWait("135");
_weeklycost = Console.ReadLine();
Grundy
  • 13,356
  • 3
  • 35
  • 55
ObiJuan
  • 77
  • 4
0

Simple solution, if user inputs nothing, assign the default:

Console.Write("Enter weekly cost: ");
string input = Console.ReadLine();
decimal weeklyCost = String.IsNullOrEmpty(input) ? 135 : decimal.Parse(input);

When dealing with user inputs, you should expect that it might contain errors. So you could use TryParse in order to avoid an exception, if the user has not input a number:

Console.Write("Enter weekly cost: ");
string input = Console.ReadLine(); 
decimal weeklyCost;
if ( !Decimal.TryParse(input, out weeklyCost) ) 
    weeklyCost = 135;

This would be considered best-practice for handling user input. If you need to parse many user inputs, use a helper function for that. One way of doing it is to use a method with a nullable and return null if parsing failed. Then it is very easy to assign a default value using the null coalescing operator:

public static class SafeConvert
{
    public static decimal? ToDecimal(string value)
    {
        decimal d;
        if (!Decimal.TryParse(value, out d))
            return null;
        return d;
    }
}

Then, to read an input and assign a default value is as easy as:

decimal d = SafeConvert.ToDecimal(Console.ReadLine()) ?? 135;
driis
  • 161,458
  • 45
  • 265
  • 341
0

You can use helper method like this:

public static string ReadWithDefaults(string defaultValue)
{
    string str = Console.ReadLine();
    return String.IsNullOrEmpty(str) ? defaultValue : str;
}
SMART_n
  • 1,070
  • 8
  • 10