Building a Menu-Driven Console Application in C#





5.00/5 (4 votes)
Discover how to create a user-friendly console application in C# and .NET Framework 4.8, with menu-driven interaction and graceful exit options.
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 namespace
s, 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
- Open Visual Studio 2022.
- Click the File menu, point to New, and click Project/Solution.
- In the Create a new project window, search for
Console App (.NET Framework)
". - Choose the
Console App (.NET Framework)
template and click Next. - Name your project
MenuDemo
and choose a location to save it. - Click Create to create the project.
Step 2: Adding Enum Definitions
-
Inside the
MenuDemo
project, add a new C# file namedEnums.cs
. -
Define an enum named
MenuChoices
to represent the menu options. Include options forEatCandy
,GoFishing
,PlayBasketball
, andExit
. Use theDescription
attribute to provide human-readable descriptions for each option.
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
:
-
Readability: Using descriptive names improves code readability. When someone reads
MenuChoices.EatCandy
, they immediately understand the intended meaning. -
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.
-
Intellisense Support: IDEs like Visual Studio provide IntelliSense support for enums, making selecting the correct value from a list of options easier.
-
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
-
Open Solution Explorer.
-
To do so, click the View menu and then click Solution Explorer.
-
-
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.
-
-
Somewhere in the
Program
class, after theMain
method, add code to define theGetUserChoice
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 theGetUserChoice
method.
-
Now, add code for the definition of the GetEnumDescription method:
- If available, this method retrieves the description associated with a given
enum
value. If noDescription
attribute is found, it returns the string representation of theenum
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 theGetCustomAttribute
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:
/// <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 theGetEnumDescription
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.
- If available, this method retrieves the description associated with a given
-
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:/// <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 theDisplayMenu
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++
incrementsmenuItemNumber
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.
-
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 theENTER
key on the keyboard to confirm.- If the confirmation is affirmative (
Y
ory
), 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.
- If the confirmation is affirmative (
- 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:/// <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 theMain
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 theMain
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:
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
- 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.
- Visual Studio should automatically build your application into a
- Follow the on-screen instructions to interact with the menu.
- 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.