0

So I've been working on a program that basically cures your boredom. (I wanted to show this program to the people at school when they use the computers).

The selection menu in the code is taken from this post:

C# Console app - How do I make an interactive menu?

But there is a bug (or more like my mistake) when you press enter on a selection, the screen clears and only refreshes when I press the up or down arrow key?

Here is the code

using System.Text;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;

namespace Awesome_C__Project
{
    internal class Program
    {
        public static List<Option> options;
        static void Main(string[] args)
        {
            Encoding UTF8 = Encoding.UTF8;
            Console.OutputEncoding = UTF8;

            // Create options that you want your menu to have
         
                options = new List<Option>
            {
                new Option("   Home      ", () => HomePageLink()),
                new Option("   Request   ", () => RequestPageLink()),
                new Option("  ⚙️ Configure ", () => ConfigurePageLink()),
                new Option("   Updates   ", () => UpdatePageLink()),
                new Option("  ❌ Exit      ", () => Environment.Exit(0)),
                
            };

            // Set the default index of the selected item to be the first
            int index = 0;

            // Write the menu out
            WriteMenu(options, options[index]);

            // Store key info in here
            ConsoleKeyInfo keyinfo;
            do
            {
                keyinfo = Console.ReadKey();

                // Handle each key input (down arrow will write the menu again with a different selected item)
                if (keyinfo.Key == ConsoleKey.DownArrow)
                {

                    if (index + 1 < options.Count)
                    {
                        index++;
                  
                        WriteMenu(options, options[index]);
                    }
                }
                if (keyinfo.Key == ConsoleKey.UpArrow)
                {
                
                    if (index - 1 >= 0)
                    {
                        index--;
                        WriteMenu(options, options[index]);

                    }
                }
                // Handle different action for the option
                if (keyinfo.Key == ConsoleKey.Enter)
                {
                    options[index].Selected.Invoke();
                    index = 0;
                    
                }
            }
            while (keyinfo.Key != ConsoleKey.X);

            Console.ReadKey();

        }
        // Default action of all the options. You can create more methods
       

        static void HomePageLink()
        {
            Console.Clear();
            options = new List<Option>
            {
                
                new Option(">  Home      ", () => HomePageLink()),
                new Option("   Request   ", () => RequestPageLink()),
                new Option("  ⚙️ Configure ", () => ConfigurePageLink()),
                new Option("   Updates   ", () => UpdatePageLink()),
                new Option("  ❌ Exit      ", () => Environment.Exit(0)),
            };
        }

        static void RequestPageLink()
        {
            Console.Clear();
            options = new List<Option>
            {

                new Option("   Home      ", () => HomePageLink()),
                new Option(">  Request   ", () => RequestPageLink()),
                new Option("  ⚙️ Configure ", () => ConfigurePageLink()),
                new Option("   Updates   ", () => UpdatePageLink()),
                new Option("  ❌ Exit      ", () => Environment.Exit(0)),
            };
        }

        static void UpdatePageLink()
        {
            Console.Clear();
            options = new List<Option>
            {

                new Option("   Home      ", () => HomePageLink()),
                new Option("   Request   ", () => RequestPageLink()),
                new Option("  ⚙️ Configure ", () => ConfigurePageLink()),
                new Option(">  Updates   ", () => UpdatePageLink()),
                new Option("  ❌ Exit      ", () => Environment.Exit(0)),
            };
        }

        static void ConfigurePageLink()
        {
            Console.Clear();
            options = new List<Option>
            {

                new Option("   Home      ", () => HomePageLink()),
                new Option("   Request   ", () => RequestPageLink()),
                new Option("> ⚙️ Configure ", () => ConfigurePageLink()),
                new Option("   Updates   ", () => UpdatePageLink()),
                new Option("  ❌ Exit      ", () => Environment.Exit(0)),
            };
        }




        static void WriteMenu(List<Option> options, Option selectedOption)
        {
            Console.Clear();

            foreach (Option option in options)
            {
                if (option == selectedOption)
                {
                    Console.Write("\u001b[48;2;50;50;50m");
                }
                else
                {
                    Console.Write("\u001b[48;2;12;12;12m");
                }

                Console.WriteLine(option.Name);
            }
        }
    }

    public class Option
    {
        public string Name { get; }
        public Action Selected { get; }

        public Option(string name, Action selected)
        {
            Name = name;
            Selected = selected;
        }
    }
}

01maxzie
  • 29
  • 8

1 Answers1

0

As @MathiasR.Jessen has already pointed out, you must call WriteMenu after the user hit Enter as well.

The are some simplifications you can make. The most striking one is that you have defined the menu entries many times. Instead define the menu options only once and keep track of the last selected index and write > in front of the corresponding menu entry using some logic. I used Console.Write(i == lastSelection ? "> " : " "); for this and removed the white spaces from the start menu options.

Also, you can use a switch statement to process keyinfo.Key to make it easier.

internal class Program
{
    private static List<Option> options;
    private static int lastSelection = -1; // Where we write the ">".

    static void Main(string[] args)
    {
        Encoding UTF8 = Encoding.UTF8;
        Console.OutputEncoding = UTF8;

        // Create options that you want your menu to have
        options = new List<Option>
        {
            new Option(" Home      ", HomePageLink),
            new Option(" Request   ", RequestPageLink),
            new Option("⚙️ Configure ", ConfigurePageLink),
            new Option(" Updates   ", UpdatePageLink),
            new Option("❌ Exit      ", () => Environment.Exit(0)),
        };

        // Set the default index of the selected item to be the first
        int index = 0;

        // Write the menu out
        WriteMenu(options, options[index]);

        // Store key info in here
        ConsoleKeyInfo keyinfo;
        do {
            keyinfo = Console.ReadKey();

            // Handle each key input (down arrow will write the menu again with a different selected item)
            switch (keyinfo.Key) {
                case ConsoleKey.DownArrow:
                    if (index + 1 < options.Count) {
                        index++;
                    }
                    break;
                case ConsoleKey.UpArrow:
                    if (index > 0) {
                        index--;
                    }
                    break;
                case ConsoleKey.Enter:
                    Console.Clear(); // The only place where we clear.
                    options[index].Selected.Invoke();
                    lastSelection = index;
                    index = 0;
                    break;
            }
            WriteMenu(options, options[index]);
        }
        while (keyinfo.Key != ConsoleKey.X);

        Console.ReadKey();
    }

    static void WriteAt(int left, int top, string text)
    {
        Console.CursorLeft = left;
        Console.CursorTop = top;
        Console.Write(text);
    }

    static void WriteMenu(List<Option> options, Option selectedOption)
    {
        // Don't clear here since we want to preserve the output the selected menu item.
        // Instead, let's use our new method WriteAt to write at a specific place.
        for (int i = 0; i < options.Count; i++) {
            Option option = options[i];
            if (option == selectedOption) {
                Console.Write("\u001b[48;2;50;50;50m");
            } else {
                Console.Write("\u001b[48;2;12;12;12m");
            }
            WriteAt(1, i, i == lastSelection ? "> " : "  ");
            Console.Write(option.Name);
        }
    }

    static void HomePageLink()
    {
        WriteAt(40, 0, "My");
        WriteAt(40, 1, "Home");
        WriteAt(40, 2, "Page");
    }

    static void RequestPageLink()
    {
        WriteAt(40, 0, "The");
        WriteAt(40, 1, "Request");
        WriteAt(40, 2, "Page");
    }

    static void UpdatePageLink()
    {
        WriteAt(40, 0, "The");
        WriteAt(40, 1, "Update");
        WriteAt(40, 2, "Page");
    }

    static void ConfigurePageLink()
    {
        WriteAt(40, 0, "The");
        WriteAt(40, 1, "Configuration");
        WriteAt(40, 2, "Page");
    }
}

class Option not shown here.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • It works fine and all, but I would like to display like content on the side when you click on one of these, how would that be possible – 01maxzie Jun 29 '23 at 07:00
  • I updated the code and introduced a new method `WriteAt`. I also removed `Console.Clear();` from `WriteMenu`, to not clear the output of any selection and the selections now display an example text. – Olivier Jacot-Descombes Jun 29 '23 at 10:19
  • Thanks! It works. You're the most helpful person I know. – 01maxzie Jun 29 '23 at 12:33