每個擁有標題欄的窗口(WS_SYSMENU 風格)都擁有一個系統菜單,通過點擊左上角圖標或 Alt+Space 觸發。C++Builder 無直接組件封裝,需使用 Win32 API 動態修改。
HMENU GetSystemMenu(HWND hWnd, BOOL bRevert);
BOOL AppendMenu(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem);
BOOL ModifyMenu(HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem);
BOOL InsertMenu(HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem);
BOOL DeleteMenu(HMENU hMenu, UINT uPosition, UINT uFlags);
void __fastcall TForm1::FormCreate(TObject *Sender) {
HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
// 添加分隔符
AppendMenu(hSysMenu, MF_SEPARATOR, 0, "");
// 添加「置頂顯示」菜單項,命令ID定為 200
AppendMenu(hSysMenu, MF_STRING, 200, "置頂顯示(&A)");
}
在單元頭文件 Unit1.h 中聲明:
#include
class TForm1 : public TForm {
__published:
// IDE 管理組件...
private:
public:
__fastcall TForm1(TComponent* Owner);
// 自定義消息處理器
MESSAGE void __fastcall SystemMenuCommand(TWMMenuSelect &Msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_SYSCOMMAND, TWMMenuSelect, SystemMenuCommand)
END_MESSAGE_MAP(TForm)
};
void __fastcall TForm1::SystemMenuCommand(TWMMenuSelect &Msg) {
// 必須先調用默認處理
TForm::Dispatch(&Msg);
if (Msg.IDItem == 200) { // 我們定義的菜單項 ID
if (FormStyle == fsNormal) {
// 切換到置頂模式
FormStyle = fsStayOnTop;
// 重建菜單並設置勾選標記
HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
AppendMenu(hSysMenu, MF_SEPARATOR, 0, "");
AppendMenu(hSysMenu, MF_STRING | MF_CHECKED, 200, "置頂顯示(&A)");
} else {
// 恢復正常模式
FormStyle = fsNormal;
// 重建菜單,移除勾選
HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
AppendMenu(hSysMenu, MF_SEPARATOR, 0, "");
AppendMenu(hSysMenu, MF_STRING, 200, "置頂顯示(&A)");
}
}
}
系統菜單保留 ID 範圍:SC_SIZE(0xF000) ~ SC_TASKLIST(0xF130)。自定義 ID 建議使用 0x8000 以上或 WM_USER (0x0400) 以上範圍。
EnableMenuItem(hSysMenu, 200, MF_BYCOMMAND | (canPin ? MF_ENABLED : MF_GRAYED));
MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
mii.fMask = MIIM_BITMAP | MIIM_ID | MIIM_TYPE;
mii.fType = MFT_BITMAP;
mii.hbmpItem = hBitmap; // HBITMAP
mii.wID = 200;
InsertMenuItem(hSysMenu, 0, TRUE, &mii);
高 DPI 下系統菜單圖標可能模糊,需在清單中聲明 dpiAware 或調用 SetProcessDpiAwarenessContext。
HMENU hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, 201, "子項1");
AppendMenu(hSubMenu, MF_STRING, 202, "子項2");
InsertMenu(hSysMenu, 0, MF_POPUP | MF_STRING, (UINT_PTR)hSubMenu, "更多選項");
protected override void WndProc(ref Message m) {
const int WM_SYSCOMMAND = 0x0112;
if (m.Msg == WM_SYSCOMMAND && (m.WParam.ToInt32() & 0xFFF0) == 0x8000) {
// 自定義系統菜單命令
if (m.WParam.ToInt32() == 200) ToggleTopMost();
return;
}
base.WndProc(ref m);
}
private void Form_Load(object sender, EventArgs e) {
IntPtr hSysMenu = GetSystemMenu(Handle, false);
AppendMenu(hSysMenu, MF_SEPARATOR, 0, null);
AppendMenu(hSysMenu, MF_STRING, 200, "置頂顯示(&A)");
}
[DllImport("user32.dll")] static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")] static extern bool AppendMenu(IntPtr hMenu, int uFlags, int uIDNewItem, string lpNewItem);
TSystemMenuHelper 類,支持鏈式調用TAction / TActionList 統一命令處理,避免硬編碼 IDFormDestroy 中調用 GetSystemMenu(Handle, TRUE) 恢復默認菜單系統菜單定制雖屬小技巧,卻能顯著提升專業軟件的用戶體驗細節。掌握 Win32 菜單 API 與 VCL 消息機制的結合,是 C++Builder 開發者進階的必修課。