Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing navigation menu in Console app

I am trying to implement complex Console menu with several multilevel sub menus(path type menu). Maybe someone suggest what design pattern to use in my situation?

Example of top menu

* Main Menu *

1. List Virtual Servers
2. List Image Templates
3. Exit

If for example 1 is selected next sub menu appears

* List Virtual Servers *

1. Virtual server #1
2. Virtual server #2
3. Virtual server #3
4. Return

If for example 2 is selected next sub menu appears

* Command for Virtual server #2 *

1. Delete
2. Return

My current navigation code is below, this is only for the first top menu, not sure how to implement multilevel path type menu with full navigation up and down through menus.

static void Main(string[] args)
{

    Console.WriteLine("1. List Virtual Servers" +
       Environment.NewLine + "2. List Image Templates" +
       Environment.NewLine + "3. Exit");

    var input = Console.ReadKey();
    var key = input.KeyChar;
    int value;
    if (int.TryParse(key.ToString(), out value))
    {
        Console.WriteLine();
        RouteChoice(value);
    }
    else
    {
        Console.WriteLine("Invalid Entry.");
    }

    Console.Write("Press any key to exit...");
    Console.ReadKey(false);

    Console.ReadLine();

}

private static void RouteChoice(int menuChoice)
{
    switch (menuChoice)
    {
        case 1:
            GetVirtualServers();
            break;
        case 2:
            GetImageTemplate();
            break;                
        default:
            Console.WriteLine("Invalid Entry!");
            break;
    }
}
like image 292
Tomas Avatar asked Sep 02 '25 17:09

Tomas


2 Answers

My idea is to model a hierarchy of menu items as a tree and to traverse this tree when a user navigates through the menu. From design patterns perspective it would be a mix of Composite and Command + some tree traversing. In order to do so we need:

  1. A base class BaseCommand to model each command in your menu.

  2. A base class Command derived from BaseCommand for menu items that performs some logic - for leafs in the tree.

  3. A base class CompositeCommand derived from BaseCommand for commands with children. This class will have Children property which will store objects of type BaseCommand.

  4. Every command that can perform some logic e.g. Virtual server #1 should be derived from Command.

  5. Every command that has children e.g. List Virtual Servers should be derived from CompositeCommand.

  6. At the beginning of your program you should create a tree of commands. In your case in the root of this tree we will find Main Menu. The Children property of Main Menu will contain references to commands for List Virtual Servers, List Image Templates, Exit and so on.

  7. The last part of this solution is a manager. The manager is a class that has to keep a track where we are in the tree. When the manager receives an input from a user, he can do 3 things: execute the current command if it is a leaf, move to the parent of the current command if a user selects Return, move to a child of the current command if it has one.

like image 181
Michał Komorowski Avatar answered Sep 04 '25 05:09

Michał Komorowski


I've written a console menu which may be useful as a starting point to you.

CMenu is a lightweight, low-ceremony framework for building console menus in .Net. Instead of manually prompting the user for input and parsing it, you define commands in a short, structured and comprehensive way, and let CMenu handle the rest.

CMenu aims for low overhead - simple stuff should be simple to implement. If you don't use a feature, you don't need to know anything about it.

At the same time, complex scenarios are supported. Large menus can easily be split into several classes. Background self-configuration. You do not have to worry about all the annoying details involved in larger menus, it will just work.

Most importantly, it is very simple and fast to use. Commands can be abbreviated, a smart parser enables even partial matching. A help command is integrated.

It was not written with this numbered style of operation in mind, but it can be emulated and should work perfectly if you name the commands "1. foo", "2. bar" etc.

The inheritance system very simple (just derive from CMenuItem and override Execute), or if you want you can even just use lambdas instead.

var menu = new CMenu ();
menu.Add ("1. foo", s => Console.WriteLine ("Foo!"));
menu.Add ("2. bar", s => Console.WriteLine ("Bar!"));
menu.Run ();

Menu trees are supported very well, see this page for examples. As an example (there are other ways to create hierarchical menus):

var m = new CMenu () {
    new CMenuItem ("1") {
        new CMenuItem ("1", s => Console.WriteLine ("1-1")),
        new CMenuItem ("2", s => Console.WriteLine ("1-2")),
    },
    new CMenuItem ("2") {
        new CMenuItem ("1", s => Console.WriteLine ("2-1")),
        new CMenuItem ("2", s => Console.WriteLine ("2-2")),
    },
};

Update: Selection via single number key press will be included natively in the next release (0.8).

like image 34
mafu Avatar answered Sep 04 '25 07:09

mafu