Click here to Skip to main content
15,904,153 members
Articles / Programming Languages / C#

Building a Menu-Driven Console Application in C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
11 May 2024GPL38 min read 5.3K   4   2
Discover how to create a user-friendly console application in C# and .NET Framework 4.8, with menu-driven interaction and graceful exit options.
This article explores how a beginner-friendly console application is developed using C# and the .NET Framework 4.8. We delve into the intricacies of creating a menu-driven interface that offers choices such as "Eat Candy," "Go Fishing," and "Play Basketball," culminating in an option to exit the application gracefully. Through a step-by-step walkthrough, we illustrate the process of writing clean, concise code while emphasizing best practices in user interaction.

Image 1
Figure 1. A screenshot of the console application displaying a menu of choices.

Contents

Introduction

This tutorial will create a simple console application using C# and the .NET Framework 4.8. Our application will display a menu of choices to the user, allowing them to perform various actions. We'll implement features such as displaying the menu, handling user input, and confirming actions like exiting the application.

This tip/trick is aimed at beginners and those just beginning to do C# programming language exercises, such as first-year computer science students.  Such a problem is frequently given to those learning to program for the first time.


NOTE: I am aware of the existence of C# 12 and .NET Core 8. However, as a personal preference, I believe that using older frameworks has more instructional value.  I am specifically choosing to use .NET Framework 4.8 and C# 7 to show students programming concepts such as namespaces, etc., that must be more explicitly declared in older versions of C#.  Furthermore, as this is not a very advanced tutorial, we can forego using a powerhouse such as .NET Core 8 when .NET Framework on Windows will do just fine, for my purposes.


Prerequisites

  • Visual Studio 2022 is installed on your machine.
  • Basic understanding of C# programming concepts.

Walkthrough

Step 1: Creating a New Console Application Project

  1. Open Visual Studio 2022.
  2. Click the File menu, point to New, and click Project/Solution.
  3. In the Create a new project window, search for Console App (.NET Framework)".
  4. Choose the Console App (.NET Framework) template and click Next.
  5. Name your project MenuDemo and choose a location to save it.
  6. Click Create to create the project.

Step 2: Adding Enum Definitions

  1. Inside the MenuDemo project, add a new C# file named Enums.cs.

  2. Define an enum named MenuChoices to represent the menu options. Include options for EatCandy, GoFishing, PlayBasketball, and Exit. Use the Description attribute to provide human-readable descriptions for each option.

C#
using System.ComponentModel;

namespace MenuDemo
{
    public enum MenuChoices
    {
        [Description("Eat Candy")]
        EatCandy,
        
        [Description("Go Fishing")]
        GoFishing,
        
        [Description("Play Basketball")]
        PlayBasketball,
        
        [Description("Exit")]
        Exit
    }
}

Listing 1. The definition of the MenuChoices enumeration.


What is an enum?

In C#, an enum (short for "enumeration") is a special data type used to define a set of named integral constants. These constants represent a finite list of possible values that a variable of that enum type can hold. Each constant in an enum has an associated integer value, which by default starts from 0 and increments by 1 for each subsequent constant.

Why are we using an enum?

In our MenuDemo application, we're using a enum called MenuChoices to represent the different options available in our menu. Instead of using raw integer values to represent each choice (e.g., 0 for "Eat Candy", 1 for "Go Fishing", and so on), we use descriptive names like EatCandy, GoFishing, etc., making our code more readable and self-explanatory.

Here are some benefits of using an enum:

  1. Readability: Using descriptive names improves code readability. When someone reads MenuChoices.EatCandy, they immediately understand the intended meaning.

  2. Type Safety: Enums provide type safety, meaning you can't assign arbitrary values to an enum variable. You're restricted to the predefined set of constants.

  3. Intellisense Support: IDEs like Visual Studio provide IntelliSense support for enums, making selecting the correct value from a list of options easier.

  4. Compile-Time Checks: The compiler performs checks to ensure that enum values are used correctly, reducing the likelihood of errors at runtime.

In summary, enums are a convenient way to define a set of related constants with meaningful names, improving code readability, maintainability, and reliability. They are particularly useful in scenarios where you have a fixed set of options or states, such as menus, application states, days of the week, etc.


Step 3: Writing the Application Logic

  1. Open Solution Explorer.

    • To do so, click the View menu and then click Solution Explorer.

  2. Expand all the items in Solution Explorer, find the Program.cs file, and double-click on it.

    • The Program.cs file is now open in the Editor.

  3. Somewhere in the Program class, after the Main method, add code to define the GetUserChoice method.

    • This method reads user input from the console.
    • It parses the input into a MenuChoices enumeration value.
    • The corresponding value is returned if the input matches any of the enumeration values.
    • If the input cannot be parsed into a valid enumeration value, it returns MenuChoices.Unknown.
    • Here's a listing of my GetUserChoice method:
      /// <summary>
      /// Reads user input from the console and parses it into a
      /// <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
      /// </summary>
      /// <returns>
      /// The <see cref="T:MenuDemo.MenuChoices" /> enumeration value corresponding to
      /// the user input.
      /// If the input cannot be parsed into a valid enumeration value, returns
      /// <see cref="F:MenuDemo.MenuChoices.Unknown" />.
      /// </returns>
      /// <remarks>
      /// This method reads a line of text from the console input and attempts to parse
      /// it into a <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
      /// <para />
      /// If the input matches any of the enumeration values, the corresponding
      /// enumeration value is returned.
      /// <para />
      /// If the input cannot be parsed into a valid enumeration value, the method
      /// returns <see cref="F:MenuDemo.MenuChoices.Unknown" />.
      /// </remarks>
      private static MenuChoices GetUserChoice()
      {
          var input = Console.ReadLine();
          return Enum.TryParse(input, out MenuChoices choice)
              ? choice
              : MenuChoices.Unknown;
      }
      Listing 2. The code of the GetUserChoice method.
  4. Now, add code for the definition of the GetEnumDescription method:

    • If available, this method retrieves the description associated with a given enum value. If no Description attribute is found, it returns the string representation of the enum value.
    • It first gets the field information of the enum value using reflection.
    • It then retrieves the DescriptionAttribute associated with the enum value, if any, using the GetCustomAttribute method.
    • If a DescriptionAttribute is found, it returns the description value associated with it.
    • If no DescriptionAttribute is found, it returns the string representation of the enum value itself.
    • Here's an example listing of my implementation of GetEnumDescription:
      C#
      /// <summary>
      /// Retrieves the description attribute value associated with the specified enum
      /// value.
      /// </summary>
      /// <param name="value">
      /// The <see langword="enum" /> value for which to retrieve the
      /// description.
      /// </param>
      /// <returns>
      /// The description associated with the <see langword="enum" /> value, if
      /// available; otherwise, the
      /// string representation of the <see langword="enum" /> value.
      /// </returns>
      /// <remarks>
      /// This method retrieves the description attribute value, if present, associated
      /// with the specified <see langword="enum" /> value.
      /// <para />
      /// If no description attribute is found, it returns the string representation of
      /// the <see langword="enum" /> value.
      /// </remarks>
      private static string GetEnumDescription(Enum value)
      {
          var field = value.GetType()
                           .GetField(value.ToString());
          var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(
              field, typeof(DescriptionAttribute)
          );
          return attribute == null ? value.ToString() : attribute.Description;
      }
      Listing 3. The code of the GetEnumDescription method.

      Purpose of the Method:
       
    • This method is used to provide human-readable descriptions for enum values.
    • It's particularly useful when displaying enum values in a user interface or logging messages.
    • By decorating enum values with DescriptionAttribute, developers can provide meaningful descriptions that enhance the readability of the code.
  5. Define the DisplayMenu method:

    • This method displays the user's menu of choices.  Each option is known as a command.
    • It iterates through all the available menu choices, excluding Unknown, and displaying them along with their corresponding numbers.
    • It prompts the user to enter their selection.
    • Here's a listing of my version of the DisplayMenu method:
      C#
      /// <summary>
      /// Displays the menu of choices on the console.
      /// </summary>
      /// <remarks>
      /// This method iterates through all the available menu choices and displays them
      /// along with their corresponding numbers.
      /// <para />
      /// The numbering of the choices starts from <c>1</c>.
      /// </remarks>
      private static void DisplayMenu()
      {
          Console.WriteLine("Please choose an action:\n");
          var menuItemNumber = 1;
          foreach (MenuChoices choice in Enum.GetValues(typeof(MenuChoices)))
              if (choice != MenuChoices.Unknown)
              {
                  var description = GetEnumDescription(choice);
                  Console.WriteLine($"[{menuItemNumber}]:    {description}");
                  menuItemNumber++;
              }
       
          Console.Write("\nEnter your selection: ");
      }
      Listing 4. The code of the DisplayMenu method.

      Purpose of the Method
       
    • This method is called within the Main method to display the menu of choices to the user.
    • It enhances the user experience by providing a clear and organized presentation of available options.
    • Presenting the choices in a numbered format helps the user easily identify and select their desired option.
    • The menuItemNumber variable is used to display the menu item number next to each choice.
    • It starts from 1 to represent the first menu choice.  Normally, the integer(s) associated with the member(s) of an enum start from zero.  Starting from 1 is a better user experience.
    • After displaying each menu choice, menuItemNumber++ increments  menuItemNumber by 1 to move to the next menu item.
    • This ensures that each menu choice is displayed with a unique and sequential number, starting from 1, making it easier for the user to find and select their desired option.
  6. Define the Main Method:

    • This is the entry point of the application.
    • It contains the main application logic, including displaying the menu, getting the user's choice, and performing actions based on that choice.
    • It runs in an infinite loop until the user chooses to exit the application.
    • If the user chooses Exit, it prompts them to confirm if they are sure they want to exit.  After typing their response, the user must press the ENTER key on the keyboard to confirm.
      • If the confirmation is affirmative (Y or y), the application terminates.
      • If the user types any other input, the console is cleared, and the menu is displayed again.
        • This is an important cybersecurity vulnerability mitigation: doing this guards against a so-called code injection attack.
    • After each action, it prompts the user to press any key to continue, clears the console, and displays the menu again.
    • Here's a listing of my version of the Main method:
      C#
      /// <summary>
      /// The entry point of the application.
      /// </summary>
      /// <remarks>
      /// This method serves as the starting point of the console application.
      /// <para />
      /// It continuously displays a menu of choices to the user and executes the
      /// corresponding actions based on their selection.
      /// <para />
      /// The menu is displayed until the user decides to exit the application.
      /// </remarks>
      public static void Main()
      {
          while (true)
          {
              DisplayMenu();
              var choice = GetUserChoice();
       
              // Convert 1-based menu choice to 0-based index
              var choiceIndex = (int)choice - 1;
       
              // Check if choice is within the valid range
              if (choiceIndex >= 0 && choiceIndex < Enum
                      .GetValues(typeof(MenuChoices))
                      .Length) // Check against all menu items
                  // Perform action based on user choice index
                  switch (choiceIndex)
                  {
                      case (int)MenuChoices.EatCandy:
                          Console.WriteLine("You chose to Eat Candy.");
       
                          // Add your Eat Candy logic here
                          break;
       
                      case (int)MenuChoices.GoFishing:
                          Console.WriteLine("You chose to Go Fishing.");
       
                          // Add your Go Fishing logic here
                          break;
       
                      case (int)MenuChoices.PlayBasketball:
                          Console.WriteLine("You chose to Play Basketball.");
       
                          // Add your Play Basketball logic here
                          break;
       
                      case (int)MenuChoices.Exit:
                          Console.Write(
                              "Are you sure you want to exit the application? (Y/N): "
                          );
                          var confirmation = Console.ReadLine()
                              .ToUpper()[0];
                          Console.WriteLine();
                          if (confirmation == 'Y')
                          {
                              Console.WriteLine("Exiting the application...");
                              return// Exit the Main method
                          }
       
                          Console.Clear();
                          continue;
       
                      default:
                          Console.WriteLine(
                              "Invalid choice. Please try again."
                          );
                          break;
                  }
              else
                  Console.WriteLine("Invalid choice. Please try again.");
       
              Console.WriteLine("Press any key to continue...");
              Console.ReadKey();
              Console.Clear(); // Clear the console for the next iteration
          }
      }
      Listing 5. The definition of the Main method.

      Purpose of the Method
       
    • The Main method serves as the entry point of a C# console application.
    • When the application is executed, the operating system looks for the Main method to start the program.
    • All the code inside the Main method is executed when the application starts.
    • It is where the execution of the program begins.
    • The Main method contains the main logic of the application.
    • It typically includes tasks such as displaying user interfaces, processing user input, and performing various actions based on user choices.
    • The Main method often involves control flow structures such as loops (while, for, foreach) and conditional statements (if, else, switch) to control the flow of execution within the application.
    • The Main method is responsible for determining when the application should terminate.
    • It usually runs in a loop until a specific condition is met, such as the user choosing to exit the application or a certain task being completed.
    • The Main method may also handle resource management tasks such as opening and closing files, database connections, or network connections.
    • Error handling code, such as try-catch blocks, may also be included in the Main method to handle exceptions that occur during the execution of the application.

Entire Program.cs File

Here's the entire Program.cs file with all the methods combined:

C#
using System;
using System.ComponentModel;
 
namespace MenuDemo
{
    /// <summary>
    /// Defines the behavior of the application.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// The entry point of the application.
        /// </summary>
        /// <remarks>
        /// This method serves as the starting point of the console application.
        /// <para />
        /// It continuously displays a menu of choices to the user and executes the
        /// corresponding actions based on their selection.
        /// <para />
        /// The menu is displayed until the user decides to exit the application.
        /// </remarks>
        public static void Main()
        {
            while (true)
            {
                DisplayMenu();
                var choice = GetUserChoice();
 
                // Convert 1-based menu choice to 0-based index
                var choiceIndex = (int)choice - 1;
 
                // Check if choice is within the valid range
                if (choiceIndex >= 0 && choiceIndex < Enum
                        .GetValues(typeof(MenuChoices))
                        .Length) // Check against all menu items
                    // Perform action based on user choice index
                    switch (choiceIndex)
                    {
                        case (int)MenuChoices.EatCandy:
                            Console.WriteLine("You chose to Eat Candy.");
 
                            // Add your Eat Candy logic here
                            break;
 
                        case (int)MenuChoices.GoFishing:
                            Console.WriteLine("You chose to Go Fishing.");
 
                            // Add your Go Fishing logic here
                            break;
 
                        case (int)MenuChoices.PlayBasketball:
                            Console.WriteLine("You chose to Play Basketball.");
 
                            // Add your Play Basketball logic here
                            break;
 
                        case (int)MenuChoices.Exit:
                            Console.Write(
                                "Are you sure you want to exit the application? (Y/N): "
                            );
                            var confirmation = Console.ReadLine()
                                .ToUpper()[0];
                            Console.WriteLine();
                            if (confirmation == 'Y')
                            {
                                Console.WriteLine("Exiting the application...");
                                return// Exit the Main method
                            }
 
                            Console.Clear();
                            continue;
 
                        default:
                            Console.WriteLine(
                                "Invalid choice. Please try again."
                            );
                            break;
                    }
                else
                    Console.WriteLine("Invalid choice. Please try again.");
 
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
                Console.Clear(); // Clear the console for the next iteration
            }
        }
 
        /// <summary>
        /// Displays the menu of choices on the console.
        /// </summary>
        /// <remarks>
        /// This method iterates through all the available menu choices and displays them
        /// along with their corresponding numbers.
        /// <para />
        /// The numbering of the choices starts from <c>1</c>.
        /// </remarks>
        private static void DisplayMenu()
        {
            Console.WriteLine("Please choose an action:\n");
            var menuItemNumber = 1;
            foreach (MenuChoices choice in Enum.GetValues(typeof(MenuChoices)))
                if (choice != MenuChoices.Unknown)
                {
                    var description = GetEnumDescription(choice);
                    Console.WriteLine($"[{menuItemNumber}]:    {description}");
                    menuItemNumber++;
                }
 
            Console.Write("\nEnter your selection: ");
        }
 
        /// <summary>
        /// Retrieves the description attribute value associated with the specified enum
        /// value.
        /// </summary>
        /// <param name="value">
        /// The <see langword="enum" /> value for which to retrieve the
        /// description.
        /// </param>
        /// <returns>
        /// The description associated with the <see langword="enum" /> value, if
        /// available; otherwise, the
        /// string representation of the <see langword="enum" /> value.
        /// </returns>
        /// <remarks>
        /// This method retrieves the description attribute value, if present, associated
        /// with the specified <see langword="enum" /> value.
        /// <para />
        /// If no description attribute is found, it returns the string representation of
        /// the <see langword="enum" /> value.
        /// </remarks>
        private static string GetEnumDescription(Enum value)
        {
            var field = value.GetType()
                             .GetField(value.ToString());
            var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(
                field, typeof(DescriptionAttribute)
            );
            return attribute == null ? value.ToString() : attribute.Description;
        }
 
        /// <summary>
        /// Reads user input from the console and parses it into a
        /// <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
        /// </summary>
        /// <returns>
        /// The <see cref="T:MenuDemo.MenuChoices" /> enumeration value corresponding to
        /// the user input.
        /// If the input cannot be parsed into a valid enumeration value, returns
        /// <see cref="F:MenuDemo.MenuChoices.Unknown" />.
        /// </returns>
        /// <remarks>
        /// This method reads a line of text from the console input and attempts to parse
        /// it into a <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
        /// <para />
        /// If the input matches any of the enumeration values, the corresponding
        /// enumeration value is returned.
        /// <para />
        /// If the input cannot be parsed into a valid enumeration value, the method
        /// returns <see cref="F:MenuDemo.MenuChoices.Unknown" />.
        /// </remarks>
        private static MenuChoices GetUserChoice()
        {
            var input = Console.ReadLine();
            return Enum.TryParse(input, out MenuChoices choice)
                ? choice
                : MenuChoices.Unknown;
        }
    }
}

Listing 6. The final content of our Program.cs file.

Step 4: Running the Application

  1. Press F5 on the keyboard, or click the Debug menu on the menu bar, and then click Start Debugging to run the application.
    • Visual Studio should automatically build your application into a .exe file, and then launch that .exe file for you. 
    • What you see should resemble -- although it may not look exactly the same -- Figure 1.
  2. Follow the on-screen instructions to interact with the menu.
  3. Test each menu option and verify that the application behaves as expected.

Conclusion

Congratulations! You've successfully created a menu-driven console application in C# using .NET Framework 4.8. You've learned to display a menu, handle user input, and implement features like confirming actions before execution. Keep exploring and experimenting with C# to enhance your programming skills further.

This article was originally posted at https://github.com/astrohart/MenuDemo

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
United States United States
Dr. Brian Hart obtained his Ph.D. in Astrophysics from the University of California, Irvine, in 2008. Under Professor David Buote, Dr. Hart researched the structure and evolution of the universe. Dr. Hart is an Astrodynamicist / Space Data Scientist with Point Solutions Group in Colorado Springs, CO, supporting Space Operations Command, United States Space Force. Dr. Hart is a Veteran of the U.S. Army and the U.S. Navy, having most recently served at Fort George G. Meade, MD, as a Naval Officer with a Cyber Warfare Engineer designator. Dr. Hart has previously held positions at Jacobs Engineering supporting Cheyenne Mountain/Space Force supporting tests, with USSPACECOM/J58 supporting operators using predictive AI/ML with Rhombus Power, and with SAIC supporting the Horizon 2 program at STARCOM. Dr. Hart is well known to the community for his over 150 technical publications and public speaking events. Originally from Minneapolis/Saint Paul, Minnesota, Dr. Hart lives in Colorado Springs with his Black Lab, Bruce, and likes bowling, winter sports, exploring, and swimming. Dr. Hart has a new movie coming out soon, a documentary called "Galaxy Clusters: Giants of the Universe," about his outer space research. The movie showcases the Chandra X-ray Observatory, one of NASA’s four great observatories and the world’s most powerful telescopes for detecting X-rays. The movie has been accepted for screening at the U.S. Air Force Academy ("USAFA" for short) Planetarium and will highlight how scientists use clusters of galaxies, the largest bound objects in the Universe, to learn more about the formation and evolution of the cosmos --- as well as the space telescopes used for this purpose, and the stories of the astronauts who launched them and the scientists who went before Dr. Hart in learning more about the nature of the Universe.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA14-May-24 3:44
professionalȘtefan-Mihai MOGA14-May-24 3:44 
GeneralMy vote of 5 Pin
Member 1335347912-May-24 4:24
Member 1335347912-May-24 4:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.