Dominic Lewis
Dominic Lewis

Reputation: 21

Why does this hang up? Menu program

class menu
{
    string[] elements;
    public menu(string[] ele) { elements = ele; }
    public menu() : this(new string[3]) { }
    public void dispMenu(int sel)
    {
        Console.Clear();
        Console.SetCursorPosition(0, 0);
        for (int i = 0; i != elements.Length; i++)
        {
            if (i != sel) { Console.WriteLine(" " + elements[i]); }
            else { Console.WriteLine(">" + elements[i] + "<"); }                
        }
    }
    public int doMenu()
    {
        int selection = 0;
        do
        {
            dispMenu(selection);
            while (!Console.KeyAvailable)
            {

            }
            if (Console.ReadKey().Key == ConsoleKey.UpArrow)
            {
                if (selection == 0) { selection = elements.Length; }
                else { selection--; }
            }
            else if (Console.ReadKey().Key == ConsoleKey.DownArrow)
            {
                if (selection == elements.Length) { selection = 0; }
                else { selection++; }
            }
        } while (Console.ReadKey(true).Key != ConsoleKey.Enter);
        return selection;
    }
}
class Program
{
    static void Main(string[] args)
    {
        menu swag = new menu(new string[3] {"Swag", "Money", "yolo"});
        Console.WriteLine(swag.doMenu());
        Console.Read();
    }
}

Why does this hang up? It's a menu object that's supposed to respond to the up and down arrows and terminate when enter is pushed, returning the selection. When run it works, however you have to press the key several times in order to get it to register. the enter key also behaves this way.

Upvotes: 1

Views: 92

Answers (3)

Dean Kuyser
Dean Kuyser

Reputation: 23

Just like a Console.ReadLine() function, it will halt the program to receive an input every time the function appears. As raderick said, you should have the ReadKey get assigned to a variable, so it's only asking for an input once.

Upvotes: 0

Lukas Kabrt
Lukas Kabrt

Reputation: 5489

The Console.ReadKey() method returns the next character or function key pressed by the user. You call this method several times in the loop, so the first call reads the first key, second call reads second key and so on. Unless you hit the correct key in the correct moment your code doesn't work.

Also the documentation states

The ReadKey method waits, that is, blocks on the thread issuing the ReadKey method, until a character or function key is pressed

so the empty loop with !Console.KeyAvailable check isn't necessary.

The code can be simplified as follows

public int doMenu() {
    int selection = 0;
    ConsoleKeyInfo key = null;

    do {
        dispMenu(selection);
        key = Console.ReadKey();

        if (key.Key == ConsoleKey.UpArrow)
        {
            if (selection == 0) { selection = elements.Length; }
            else { selection--; }
        }
        else if (key.Key == ConsoleKey.DownArrow)
        {
            if (selection == elements.Length) { selection = 0; }
            else { selection++; }
        }
    } while (key.Key != ConsoleKey.Enter);
    return selection;
}

Upvotes: 1

Red
Red

Reputation: 2776

Assign Console.ReadKey() result into a variable once before doing all comparisons. Otherwise I believe it would try to read input every time a condition is hit. Right now you do it three times, so first time you hit a key it will only compare for a condition for UpArrow, second time - for DownArrow, third time - only for Enter, so you have to hit a key several times till it's handled. Something like this for your doMenu method:

public int doMenu()
{
    int selection = 0;
    ConsoleKeyInfo consoleKeyInfo;
    do
    {
        dispMenu(selection);
        while (!Console.KeyAvailable)
        {

        }
        consoleKeyInfo = Console.ReadKey();
        if (consoleKeyInfo.Key == ConsoleKey.UpArrow)
        {
            if (selection == 0) { selection = elements.Length; }
            else { selection--; }
        }
        else if (consoleKeyInfo.Key == ConsoleKey.DownArrow)
        {
            if (selection == elements.Length) { selection = 0; }
            else { selection++; }
        }
    } while (consoleKeyInfo.Key != ConsoleKey.Enter);
    return selection;
}

Upvotes: 1

Related Questions