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

WTL 動態選單實作:從 CTreeViewCtrl 節點生成右鍵子選單

     編輯:關於C語言

在 WTL 專案中,常需根據樹狀檢視(CTreeViewCtrl)的節點結構,動態建立右鍵選單的子選單項目。本文提供完整實作步驟與代碼片段。

應用場景

  • 樹狀列表根節點下的子節點代表「群組」。
  • 右鍵點選某葉子節點(如好友),彈出選單包含「移動至群組」子選單。
  • 子選單項目需動態反映當前樹狀結構的群組名稱。

四步驟完整實作

1. 刪除舊有動態子選單項目(關鍵步驟)


// 取得右鍵選單的第 0 個子選單(主選單),再取得其第 MY_MOVE_FRIENT_MENU_POS 個子選單(目標子選單)
CMenuHandle menu = pMenu->GetSubMenu(0).GetSubMenu(MY_MOVE_FRIENT_MENU_POS);

// 從後往前刪除,避免索引偏移
for (int i = menu.GetMenuItemCount(); i > 0; --i)
{
    menu.RemoveMenu(i, MF_BYPOSITION);
}

注意:動態新增的選單項目不會自動銷毀,每次彈出前必須先清空,否則項目會重複疊加。

2. 讀取樹狀檢視的第一層子節點(群組節點)


// 取得根節點
HTREEITEM hRoot = m_wndLocalList.GetRootItem();
// 取得第一個子節點(第一個群組)
HTREEITEM hFirst = m_wndLocalList.GetChildItem(hRoot);
CString strFirst;
if (hFirst)
    m_wndLocalList.GetItemText(hFirst, strFirst);

3. 遍歷節點並插入子選單


int nMenuID = 0; // 動態分配的命令 ID 起始偏移
while (hFirst != NULL)
{
    // 略過「目前所屬群組」(不需移動到自己所在群組)
    if (strFirst.Compare(strItemSelParent) != 0)
    {
        // 插入選單項目:位置 -1(尾部)、按位置、字串類型、動態 ID、顯示文字
        menu.InsertMenu(-1, MF_BYPOSITION | MF_STRING,
                        (UINT)(ID_MENU_ADD_FRIEND_TO_GROUP + nMenuID),
                        strFirst);
        ++nMenuID;
    }

    // 移至下一個同層節點
    hFirst = m_wndLocalList.GetNextItem(hFirst, TVGN_NEXT);
    if (hFirst)
        m_wndLocalList.GetItemText(hFirst, strFirst);
}

4. 訊息映射處理動態命令範圍

在訊息映射巨集區塊加入:


BEGIN_MSG_MAP(YourDialogClass)
    // ... 其他映射 ...
    COMMAND_RANGE_HANDLER(ID_MENU_ADD_FRIEND_TO_GROUP,
                          ID_MENU_ADD_FRIEND_TO_GROUP + 100,
                          OnMenuAddToFriendGroupRange)
END_MSG_MAP()
  • COMMAND_RANGE_HANDLER 將連續 100 個 ID 導向同一處理函式。
  • ID_MENU_ADD_FRIEND_TO_GROUP 定義為 const int,需確保不與 resource.h 衝突。

5. 處理函式實作


LRESULT OnMenuAddToFriendGroupRange(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
    // 1. 取得使用者點選的葉子節點
    HTREEITEM hItemSel = m_wndLocalList.GetSelectedItem();
    if (!hItemSel) return 0;

    CString strSelectedText;
    m_wndLocalList.GetItemText(hItemSel, strSelectedText);

    // 2. 從樹狀列表移除該節點
    m_wndLocalList.DeleteItem(hItemSel);

    // 3. 根據 wID 反推目標群組節點 hGroupItem
    //    (需遍歷群組節點比對 ID_MENU_ADD_FRIEND_TO_GROUP + index == wID)
    HTREEITEM hGroupItem = FindGroupItemByMenuID(wID);

    // 4. 插入到目標群組下(按字母排序 TVI_SORT)
    HTREEITEM hNewItem = m_wndLocalList.InsertItem(strSelectedText, hGroupItem, TVI_SORT);

    // 5. 通知伺服器更新群組關係
    add_to_group();

    return 0;
}

關鍵 API 快查表

API用途
CMenuHandle::GetSubMenu(int)取得子選單控制碼
CMenuHandle::GetMenuItemCount()取得選單項目數
CMenuHandle::RemoveMenu(UINT, UINT)移除項目 (MF_BYPOSITION / MF_BYCOMMAND)
CMenuHandle::InsertMenu(UINT, UINT, UINT, LPCTSTR)插入項目
CTreeViewCtrl::GetRootItem()取得根節點
CTreeViewCtrl::GetChildItem(HTREEITEM)取得第一個子節點
CTreeViewCtrl::GetNextItem(HTREEITEM, UINT)取得下一個節點 (TVGN_NEXT 同層)
CTreeViewCtrl::GetItemText(HTREEITEM, CString&)取得節點文字
CTreeViewCtrl::InsertItem(LPCTSTR, HTREEITEM, HTREEITEM)插入節點
COMMAND_RANGE_HANDLER(idFirst, idLast, handler)WTL 巨集:命令 ID 範圍映射

常見坑點提醒

  • 忘記清空舊選單:導致重複項目疊加,記憶體洩漏。
  • ID 範圍不足:+100 若群組超過 100 個會溢位,動態計算上限或擴大範圍。
  • 樹狀控制項未設定 TVS_TRACKSELECT:右鍵未自動選中節點,需手動 SelectItem。
  • Unicode/ANSI 混用:專案為 Unicode 時,CString、LPCTSTR 皆為寬字元,插入選單無需轉換。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved