C++控制台菜单优化:实现箭头键选择与选中项文字变色
实现上下箭头切换的高亮选中主菜单(C++ Windows)
要实现上下箭头切换菜单并高亮选中项,我们需要利用Windows控制台API控制文本颜色,同时重构菜单交互逻辑,具体实现如下:
核心修改点
- 使用
SetConsoleTextAttribute设置文本颜色,通过GetStdHandle获取控制台句柄 - 将菜单选项封装为数组,便于管理选中状态
- 监听上下箭头按键(
VK_UP/VK_DOWN)循环切换选中索引 - 回车确认选中项,执行对应功能
完整修改后的代码
#include <iomanip> #include <iostream> #include <string.h> #include <Windows.h> #include <conio.h> #include <bits/stdc++.h> #include <vector> #include <fstream> using namespace std; vector<pair<int, string>> days{{0,"SATURDAY"}, {1, "SUNDAY"}, {2,"MONDAY"}, {3,"TUESDAY"}, {4,"WEDNESDAY"}, {5,"THURSDAY"}, {6,"FRIDAY"}}; // 定义菜单选项结构体 struct MenuItem { int id; string text; }; // 获取控制台句柄,用于设置文本颜色 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); bool validDate(int d, int m, int y, char c) { if(c == '/') { if(y >= 1 && y <= 2999) { if(m >= 1 && m <= 12) { if(d >= 1 && d <= 31) { if( (d >= 1 && d <= 30) && (m == 4|| m == 6|| m == 9|| m == 11)) return true; else if((d >= 1 && d <= 31) && (m == 1 || m == 3 || m == 5 || m == 7 || m == 8|| m == 10 ||m == 12)) return true; else if((d >= 1 && d <= 28) && (m == 2)) return true; else if(d == 29 && m == 2 && ((y % 400 == 0)||(y % 4 == 0 && y % 100 != 0))) return true; else return false; } else return false; } else return false; } else return false; } else return false; } int ZellerCongruence(int givenday, int givenmonth, int givenyear) { if(givenmonth == 1) { givenmonth = 13; givenyear--; } if(givenmonth == 2) { givenmonth = 14; givenyear--; } int q = givenday; int m = givenmonth; int k = givenyear % 100; int j = givenyear / 100; int h = q + (13 * (m + 1) / 5) + k + (k / 4) + (j / 4) + (5 * j); h = h % 7; return h; } void SubMenu1() { cout << endl << "FIND THE DAY OF THE WEEK FROM THE GIVEN DATE" << endl; cout << endl << "Notice: Program can only calculate the year from 1 to 2999" << endl; cout << endl << "Enter date in dd/mm/yyyy format and press enter: "; int givenday, givenmonth, givenyear; char c; cin >> givenday; cin >> c; cin >> givenmonth; cin >> c; cin >> givenyear; if(validDate(givenday, givenmonth, givenyear, c) == false) { cout << "Invalid Date" << endl; Sleep(2000); return; } int h = ZellerCongruence(givenday, givenmonth, givenyear); cout << endl << "The given day of date is: " << days[h].second << endl; } void SubMenu2() { cout << "FIND THE DAY OF THE WEEK AFTER N DAYS FROM THE GIVEN DATE" << endl; cout << endl << "Notice: Program can only calculate the year from 1 to 2999" << endl; cout << endl << "Enter date in dd/mm/yyyy format and press enter: "; int givenday, givenmonth, givenyear; char c; cin >> givenday; cin >> c; cin >> givenmonth; cin >> c; cin >> givenyear; if(validDate(givenday, givenmonth, givenyear, c) == false) { cout << "Invalid Date" << endl; Sleep(2000); return; } int h = ZellerCongruence(givenday, givenmonth, givenyear); int n; cout << "Enter the value of n: "; cin >> n; if(n <= 0) { cout << "Invalid value, n must be a positive integer." << endl; Sleep(2000); return; } h = (h + n) % 7; cout << "The day of the week after " << n << " days from the given date is: " << days[h].second << endl; } // 绘制菜单,高亮选中项 void DrawMenu(const vector<MenuItem>& menuItems, int selectedIndex) { system("CLS"); cout << endl << "MAIN MENU" << endl << endl; for(int i = 0; i < menuItems.size(); i++) { if(i == selectedIndex) { // 设置高亮颜色(白底黑字) SetConsoleTextAttribute(hConsole, BACKGROUND_WHITE | FOREGROUND_BLACK); cout << menuItems[i].text << endl; // 恢复默认颜色(黑底白字) SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } else { cout << menuItems[i].text << endl; } } cout << endl << "Use ↑↓ arrows to navigate, Enter to select" << endl; } int main() { // 定义菜单选项列表 vector<MenuItem> menuItems = { {1, "1. Find the day of the week from the given date"}, {2, "2. Find the day of the week after n days from the given date"}, {0, "0. Quit the program"} }; int selectedIndex = 0; int choice; while(true) { DrawMenu(menuItems, selectedIndex); char key = getch(); // 处理箭头键,getch()会先返回0或224,再返回实际键值 if(key == 0 || key == 224) { key = getch(); if(key == VK_UP) { selectedIndex = (selectedIndex - 1 + menuItems.size()) % menuItems.size(); } else if(key == VK_DOWN) { selectedIndex = (selectedIndex + 1) % menuItems.size(); } } else if(key == 13) // 回车键确认选择 { choice = menuItems[selectedIndex].id; system("CLS"); switch(choice) { case 0: cout << "See you again !" << endl; return 0; case 1: SubMenu1(); system("pause"); break; case 2: SubMenu2(); system("pause"); break; default: cout << "Invalid Number..." << endl; system("pause"); break; } } } return 0; }
关键说明
- 颜色控制:通过
SetConsoleTextAttribute设置控制台文本属性,选中项使用白底黑字突出显示,其他项保持默认黑底白字。 - 菜单绘制:每次按键后重新绘制菜单,确保选中项的高亮状态正确更新。
- 按键处理:箭头键会触发两次
getch()调用(先返回前缀码,再返回实际键值),需要捕获VK_UP和VK_DOWN来切换选中索引,回车确认选择。 - 循环切换:使用取模运算实现菜单选项的循环切换,避免索引越界。
内容的提问来源于stack exchange,提问作者Hoang Tu Bong Dem
相关产品推荐
相关产品推荐

