Starbeamrainbowlabs

Stardust
Blog


Archive

Mailing List Articles Atom Feed Comments Atom Feed Twitter

Tag Cloud

3d account algorithms announcement archives arduino artificial intelligence assembly async audio bash batch blog bookmarklet booting c sharp c++ challenge chrome os code codepen coding conundrums coding conundrums evolved command line compiling css dailyprogrammer debugging demystification distributed computing downtime embedded systems encryption es6 features event experiment external first impressions future game github github gist graphics hardware hardware meetup holiday html html5 html5 canvas interfaces internet io.js jabber javascript js bin labs learning library linux low level lua maintenance network networking node.js operating systems performance photos php pixelbot portable privacy programming problems project projects prolog protocol protocols pseudo 3d python reddit reference release releases resource review rust secrets security series list server software sorting source code control statistics svg technical terminal textures three thing game three.js tool tutorial tutorials twitter ubuntu university update updates upgrade version control visual web website windows windows 10 xmpp

Easy C♯ Menus

I have written some easy to use C♯ menus, and I thought that I would post about them here.

There are currently 2 different methods (and 2 extra helper methods). Both methods also centre the dialog box in the middle of the console, and also don't produce much mess that needs cleaning up afterwards.

Firstly, here are the two helper methods:

//from https://stackoverflow.com/questions/17590528/pad-left-pad-right-pad-center-string
///<summary>
///Cool function from stackoverflow that pads a string on both sides to make it a given legnth.
///</summary>
///<param name="source">The source string to pad.</param>
///<param name="length">The desired length.</param>
///<returns>The padded string.</returns>
static string PadBoth(string source, int length)
{
    int spaces = length - source.Length;
    int padLeft = spaces/2 + source.Length;
    return source.PadLeft(padLeft).PadRight(length);
}

//utility function that uses the above to pad a string to the current width of the console.
static string PadToWindowWidth(string str)
{
    return PadBoth(str, Console.WindowWidth - 1);
}

These need to be included in addition to either (or both!) of the methods described below.

A screenshot of the first menu type. The first one is a flexible multiple choice selection window. You can pass in an array of string sthat you want the user to choose from, and the method will deal with the rest, returning the index in the array of the item that the user chose.

This method comes with support for an optional prompt to display at the top of the dialog box. If you omit it, the prompt will not be displayed.

Source code:

///<summary>
///Displays a nice multiple choice menu that the user can intract with using the arrow keys.
///</summary>
///<param name="options">An array of strings that should be used as the possible options in the menu.</param>
///<returns>The index of the option the user chose.</returns>
static int DisplayMenu(string[] options, string prompt = "")
{
    int cursorstartx = Console.CursorLeft,
        cursorstarty = Console.CursorTop,

        menuWidth = (int)(Console.WindowWidth * 0.8),
        menuHeight = 2 + (options.Length * 2) + 1;

    if(prompt.Length > 0)
        menuHeight += 2;

    int currentIndex = 0;

    string menu = "";

    while(true)
    {
        Console.SetCursorPosition(cursorstartx, cursorstarty);

        menu = "";
        menu += new String('\n', ((Console.WindowHeight - 1) - menuHeight) / 2);
        menu += PadToWindowWidth("".PadLeft(menuWidth, '-')) + "\n";

        if(prompt.Length > 0)
        {
            menu += PadToWindowWidth("| " + PadBoth(prompt, menuWidth - 4) + " |") + "\n";
            menu += PadToWindowWidth("".PadLeft(menuWidth, '-')) + "\n";
        }

        menu += PadToWindowWidth("|" + "".PadLeft(menuWidth - 2) + "|") + "\n";

        for(int i = 0; i < options.Length; i++)
        {
            if(currentIndex == i)
            {
                menu += PadToWindowWidth("|" + PadBoth("> " + options[i] + " <", menuWidth - 2) + "|") + "\n";
            }
            else
            {
                menu += PadToWindowWidth("|" + PadBoth(options[i], menuWidth - 2) + "|") + "\n";
            }
            menu += PadToWindowWidth("|" + "".PadLeft(menuWidth - 2) + "|") + "\n";
        }
        menu += PadToWindowWidth("".PadLeft(menuWidth, '-'));
        menu += new String('\n', ((Console.WindowHeight - 1) - menuHeight) / 2);

        Console.WriteLine(menu);

        ConsoleKeyInfo nextkey = Console.ReadKey(true);

        switch(nextkey.Key.ToString())
        {
            case "UpArrow":
                currentIndex--;
                break;

            case "DownArrow":
                currentIndex++;
                break;

            case "Enter":
                return currentIndex;
        }

        if(currentIndex < 0)
            currentIndex = options.Length - 1;
        if(currentIndex > options.Length - 1)
            currentIndex = 0;
    }
}

Confirm Dialog

A screenshot of the second menu type.

In case you want to obtain an answer to a simple yes/no question, this second method allows you to ask the user to choose between 2 choices. Simply specify a prompt, and optionally the text to display in the place of the "Yes" / "No", and the method will return true or false, depending on which one the user selected.

Source code:

///<summary>
///Asks the user a simple yes/no question in the form of a console based dialog box.
///</summary>
///<param name="prompt">The question to ask the user.</param>
///<param name="trueText">The text to display in the place of "Yes"</param>
///<param name="falseText">The text to display in the place of "No"</param>
///<returns>True if the user selected "Yes", or false if the user selected "No".</returns>
static bool DisplayConfirm(string prompt, string trueText = "Yes", string falseText = "No")
{
    int cursorstartx = Console.CursorLeft,
        cursorstarty = Console.CursorTop,

        menuWidth = (int)(Console.WindowWidth * 0.8),
        menuHeight = 7,

        currentIndex = 1;

    string menu = "";

    while(true)
    {
        Console.SetCursorPosition(cursorstartx, cursorstarty);

        menu = "";
        menu += new String('\n', ((Console.WindowHeight - 1) - menuHeight) / 2); //vertical centring

        menu += PadToWindowWidth("".PadLeft(menuWidth, '-')) + "\n"; //dashes
        menu += PadToWindowWidth("|" + "".PadLeft(menuWidth - 2) + "|") + "\n"; //space

        menu += PadToWindowWidth("|" + PadBoth(prompt, menuWidth - 2) + "|") + "\n"; //prompt
        menu += PadToWindowWidth("|" + "".PadLeft(menuWidth - 2) + "|") + "\n"; //space
        menu += PadToWindowWidth("".PadLeft(menuWidth, '-')) + "\n"; //dashes

        if(currentIndex == 0)
            menu += PadToWindowWidth("|" + PadBoth(trueText, (menuWidth - 3) / 2) + "|" + PadBoth("> " + falseText + " <", (menuWidth - 3) / 2) + "|") + "\n";
        else
            menu += PadToWindowWidth("|" + PadBoth("> " + trueText + " <", (menuWidth - 3) / 2) + "|" + PadBoth(falseText, (menuWidth - 3) / 2) + "|") + "\n";

        menu += PadToWindowWidth("".PadLeft(menuWidth, '-')) + "\n"; //dashes

        menu += new String('\n', ((Console.WindowHeight - 1) - menuHeight) / 2); //vertical centring

        Console.WriteLine(menu);

        ConsoleKeyInfo nextkey = Console.ReadKey(true);

        switch(nextkey.Key.ToString())
        {
            case "LeftArrow":
            case "UpArrow":
                currentIndex--;
                break;

            case "RightArrow":
            case "DownArrow":
                currentIndex++;
                break;

            case "Enter":
                if(currentIndex == 0)
                    return true;
                else
                    return false;
        }

        if(currentIndex < 0)
            currentIndex = 1;
        if(currentIndex > 1)
            currentIndex = 0;
    }
}

I will probably write a few more of these in the future, and make these current ones better.

Demonstration binaries are available upon request, simply leave a comment below.

Art by Mythdael