首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++Builder 系統菜單定制:動態添加「置頂顯示」功能項

     編輯:VC 教學

系統菜單基礎

每個擁有標題欄的窗口(WS_SYSMENU 風格)都擁有一個系統菜單,通過點擊左上角圖標或 Alt+Space 觸發。C++Builder 無直接組件封裝,需使用 Win32 API 動態修改。

核心 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);

完整實現步驟

1. 窗口創建時添加菜單項

void __fastcall TForm1::FormCreate(TObject *Sender) {
    HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
    
    // 添加分隔符
    AppendMenu(hSysMenu, MF_SEPARATOR, 0, "");
    
    // 添加「置頂顯示」菜單項,命令ID定為 200
    AppendMenu(hSysMenu, MF_STRING, 200, "置頂顯示(&A)");
}

2. 消息映射處理 WM_SYSCOMMAND

在單元頭文件 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)
};

3. 處理邏輯:切換置頂狀態與菜單檢查標記

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)");
        }
    }
}

進階技巧與避坑指南

1. 菜單項 ID 衝突避免

系統菜單保留 ID 範圍:SC_SIZE(0xF000) ~ SC_TASKLIST(0xF130)。自定義 ID 建議使用 0x8000 以上或 WM_USER (0x0400) 以上範圍。

2. 動態啟用/禁用菜單項

EnableMenuItem(hSysMenu, 200, MF_BYCOMMAND | (canPin ? MF_ENABLED : MF_GRAYED));

3. 圖標菜單項(Owner-Draw)

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);

4. 多顯示器 DPI 感知

高 DPI 下系統菜單圖標可能模糊,需在清單中聲明 dpiAware 或調用 SetProcessDpiAwarenessContext。

5. 彈出子菜單

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, "更多選項");

C# WinForms 等效實現

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);

工程化建議

  1. 將菜單構建邏輯封裝為 TSystemMenuHelper 類,支持鏈式調用
  2. 使用 TAction / TActionList 統一命令處理,避免硬編碼 ID
  3. 在 FormDestroy 中調用 GetSystemMenu(Handle, TRUE) 恢復默認菜單
  4. 單元測試覆蓋:菜單項增刪、狀態切換、多語言文本、高DPI渲染

系統菜單定制雖屬小技巧,卻能顯著提升專業軟件的用戶體驗細節。掌握 Win32 菜單 API 與 VCL 消息機制的結合,是 C++Builder 開發者進階的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved