'Advanced' Console Application
Asked Answered
D

8

17

I'm not sure if this question has been answered elsewhere and I can't seem to find anything through google that isn't a "Hello World" example... I'm coding in C# .NET 4.0.

I'm trying to develop a console application that will open, display text, and then wait for the user to input commands, where the commands will run particular business logic.

For example: If the user opens the application and types "help", I want to display a number of statements etc etc. I'm not sure how to code the 'event handler' for user input though.

Hopefully this makes sense. Any help would be much appreciated! Cheers.

Dimer answered 5/1, 2011 at 14:36 Comment(2)
Code a simple application that says "Bad command" no matter what the user types. Then post your code and make your question more specific.Blowpipe
I definitely would, but I don't really have any code because I'm not sure where to start...Dimer
M
23

You need several steps to achieve this but it shouldn't be that hard. First you need some kind of parser that parses what you write. To read each command just use var command = Console.ReadLine(), and then parse that line. And execute the command... Main logic should have a base looking this (sort of):

public static void Main(string[] args)
{
    var exit = false;
    while(exit == false)
    {
         Console.WriteLine();
         Console.WriteLine("Enter command (help to display help): "); 
         var command = Parser.Parse(Console.ReadLine());
         exit = command.Execute();
    }
}

Sort of, you could probably change that to be more complicated.

The code for the Parser and command is sort of straight forward:

public interface ICommand
{
    bool Execute();
}

public class ExitCommand : ICommand
{
    public bool Execute()
    {
         return true;
    }
}

public static Class Parser
{
    public static ICommand Parse(string commandString) { 
         // Parse your string and create Command object
         var commandParts = commandString.Split(' ').ToList();
         var commandName = commandParts[0];
         var args = commandParts.Skip(1).ToList(); // the arguments is after the command
         switch(commandName)
         {
             // Create command based on CommandName (and maybe arguments)
             case "exit": return new ExitCommand();
               .
               .
               .
               .
         }
    }
}
Mcconnell answered 5/1, 2011 at 14:42 Comment(11)
Great, thanks. This is definitely the sort of thing I'm looking forDimer
Sorry, how does the interface work in this case? I haven't worked with interfaces much, so that may be my issue...Dimer
The interface just specify which method a ceratin object should have. With that in place you can define several different objects that inherits from the interface and let your parser create those objects for you. You can change the interface to what ever you feel like, I just did it with a simple Execute that returns true if the program should exit. I can update with a simple command and sample.Mcconnell
Thank you... and one good thing with this solution is that every Command "lives" in their own context sort of. So nothing stops a command from prompting the user with more questions.Mcconnell
When using your code though Tomas, when I try to build it I get an error saying that I can't declare an instance of a class (ExitCommand) in a static class... any thoughts?Dimer
Not really, I guess you already change the method signature to static for the Parser method. Could you give the exact exception?Mcconnell
I'll give that a shot. This is the error: Error 1 'Parse': cannot declare instance members in a static classDimer
Ok, then I think you need to add the static keyword to the Parse method, I updated the code to reflect that before previous comment.Mcconnell
Hello Mister @TomasJansson, is there a way to do dependency injection for new commands and attach them to the parser?. ^-^ Thanks for reply.Feed
@JeancarloFontalvo, note that the code above was something I wrote in a matter of minutes a couple of years back. What I would do is to add all the commands in a dictionary of string to commands (or command line parser if you need to parse more input to create the command). If you have that in place you should be able to do: dict[commandName](args) instead of the switch/case.Mcconnell
Yeah Mister @TomasJansson, nice approach. ThanksFeed
A
2

I know this is an old question, but I was searching for an answer too. I was unable to find a simple one though, so I built InteractivePrompt. It's available as a NuGet Package and you can easily extend the code which is on GitHub. It features a history for the current session also.

The functionality in the question could be implemented this way with InteractivePrompt:

static string Help(string strCmd)
{
    // ... logic
    return "Help text";
}
static string OtherMethod(string strCmd)
{
    // ... more logic
    return "Other method";
}
static void Main(string[] args)
{
    var prompt = "> ";
    var startupMsg = "BizLogic Interpreter";
    InteractivePrompt.Run(
        ((strCmd, listCmd) =>
        {
            string result;
            switch (strCmd.ToLower())
            {
                case "help":
                    result = Help(strCmd);
                    break;
                case "othermethod":
                    result = OtherMethod(strCmd);
                    break;
                default:
                    result = "I'm sorry, I don't recognize that command.";
                    break;
            }

            return result + Environment.NewLine;
        }), prompt, startupMsg);
}
Agronomics answered 15/3, 2017 at 17:9 Comment(0)
R
0

This is easy enough, just use the Console.WriteLine and Console.ReadLine() methods. From the ReadLine you get a string. You could have a horrible if statement that validate this against known/expected inputs. Better would be to have a lookup table. Most sophisticated would be to write a parser. It really depends on how complex the inputs can be.

Rickety answered 5/1, 2011 at 14:41 Comment(1)
but with the Console.ReadLine() method, how do I then code the different permutations of answers etc? For example Console.ReadLine(), if(Console.ReadLine() == "help) { } etc etcDimer
C
0

Console.WriteLine Console.ReadLine and Console.ReadKey are your friends. ReadLine and ReadKey waits for user input. The string[] args will have all of your parameters such as 'help' in them. The array is created by separating the command line arguments by spaces.

Cotillion answered 5/1, 2011 at 14:42 Comment(0)
E
0
switch (Console.ReadLine())
{
    case "Help":
        // print help
        break;

    case "Other Command":
        // do other command
        break;

    // etc.

    default:
        Console.WriteLine("Bad Command");
        break;
}

If you're looking to parse commands that have other things in them like parameters, for example "manipulate file.txt" then this alone won't work. But you could for example use String.Split to separate the input into a command and arguments.

Emelda answered 5/1, 2011 at 14:43 Comment(0)
A
0

This is very simplistic, but might meet your needs.

// somewhere to store the input
string userInput="";

// loop until the exit command comes in.
while (userInput != "exit")
{
    // display a prompt
    Console.Write("> ");
    // get the input
    userInput = Console.ReadLine().ToLower();

    // Branch based on the input
    switch (userInput)
    {
        case "exit": 
          break;

        case "help": 
        {
          DisplayHelp(); 
          break;
        }

        case "option1": 
        {
          DoOption1(); 
          break;
        }

        // Give the user every opportunity to invoke your help system :)
        default: 
        {
          Console.WriteLine ("\"{0}\" is not a recognized command.  Type \"help\" for options.", userInput);
          break;
        }
    }
}
Androgynous answered 5/1, 2011 at 14:50 Comment(0)
L
0

A sample:

    static void Main(string[] args)
    {
        Console.WriteLine("Welcome to test console app, type help to get some help!");

        while (true)
        {
            string input = Console.ReadLine();

            int commandEndIndex = input.IndexOf(' ');

            string command = string.Empty;
            string commandParameters = string.Empty;

            if (commandEndIndex > -1)
            {
                command = input.Substring(0, commandEndIndex);
                commandParameters = input.Substring(commandEndIndex + 1, input.Length - commandEndIndex - 1);
            }
            else
            {
                command = input;
            }

            command = command.ToUpper();

            switch (command)
            {
                case "EXIT":
                    {
                        return;
                    }
                case "HELP":
                    {
                        Console.WriteLine("- enter EXIT to exit this application");
                        Console.WriteLine("- enter CLS to clear the screen");
                        Console.WriteLine("- enter FORECOLOR value to change text fore color (sample: FORECOLOR Red) ");
                        Console.WriteLine("- enter BACKCOLOR value to change text back color (sample: FORECOLOR Green) ");
                        break;
                    }
                case "CLS":
                    {
                        Console.Clear();
                        break;
                    }

                case "FORECOLOR":
                    {
                        try
                        {
                            Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), commandParameters);
                        }
                        catch
                        {
                            Console.WriteLine("!!! Parameter not valid");
                        }

                        break;
                    }
                case "BACKCOLOR":
                    {
                        try
                        {
                            Console.BackgroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), commandParameters);
                        }
                        catch
                        {
                            Console.WriteLine("!!! Parameter not valid"); 
                        }

                        break;
                    }
                default:
                    {
                        Console.WriteLine("!!! Bad command");
                        break;
                    }
            }
        }
    }
Lauritz answered 5/1, 2011 at 14:53 Comment(3)
that is not a good design since your function is doing ALL the work. You should divide in so every part has one responsibility.Mcconnell
i agree with that, but i figured out that he is looking for code that he can learn from and that is not too much complicated. You are correct that logic should be separated.. At the end both samples your and mine gives a same result.Lauritz
It definitely has the same result in the end, but I don't think my solution is that more complicated and it is important that you learn how to do things the right way.... even though mine is probably not suitable for everyone but design is something you should think of. I even consider my solution easier since it is much more "fluent" when you read it and you skip all the details if you don't need them. On a high level it s only, read command, parse the command and last execute the command. That is, you can skip the details in how the command is parsed and how it is executed.Mcconnell
U
0

There is a C# nuget package called 'ReadLine' by 'tornerdo'. The statement ReadLine.Read(" prompt > "); prompts the user within options provided in CustomAutoCompletionHandler.PossibleAutoCompleteValues.

Additionally, you can change the CustomAutoCompletionHandler.PossibleAutoCompleteValues for each prompt. This ensures that the user get to choose an option from available\ supported list of options. Less error prone.

static void Main(string[] args)
{
    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.WriteLine(" Note! When it prompts, press <tab> to get the choices. Additionally, you can use type ahead search.");
    Console.ForegroundColor = ConsoleColor.White;

    // Register auto completion handler..
    ReadLine.AutoCompletionHandler = new CustomAutoCompletionHandler();

    CustomAutoCompletionHandler.PossibleAutoCompleteValues = new List<string> { "dev", "qa", "stg", "prd" };
    var env = CoverReadLine(ReadLine.Read("  Environment > "));
    Console.WriteLine($"Environment: {env}");
}

private static string CoverReadLine(string lineRead) => CustomAutoCompletionHandler.PossibleAutoCompleteValues.Any(x => x == lineRead) ? lineRead : throw new Exception($"InvalidChoice. Reason: No such option, '{lineRead}'");
        
public class CustomAutoCompletionHandler : IAutoCompleteHandler
{
    public static List<string> PossibleAutoCompleteValues = new List<string> { };

    // characters to start completion from
    public char[] Separators { get; set; } = new char[] { ' ', '.', '/' };

    // text - The current text entered in the console
    // index - The index of the terminal cursor within {text}
    public string[] GetSuggestions(string userText, int index)
    {
        var possibleValues = PossibleAutoCompleteValues.Where(x => x.StartsWith(userText, StringComparison.InvariantCultureIgnoreCase)).ToList();
        if (!possibleValues.Any()) possibleValues.Add("InvalidChoice");
        return possibleValues.ToArray();
    }
}
Uzzia answered 17/3, 2022 at 16:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.