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

MFC CListCtrl 子項編輯完整實作:單點高亮、雙點編輯

     編輯:關於VC++

MFC CListCtrl 僅支援標籤(第 0 欄)編輯(需設定 LVS_EDITLABELS),子項無法直接編輯。本文實作一個 CEditListCtrl 擴充類別,採用「單擊高亮、再單擊編輯」的直覺操作模式,完整支援主項與子項的即時編輯,並解決編輯框定位、焦點管理、重繪閃爍等細節問題。

核心設計思路

  • 自訂繪製 (NM_CUSTOMDRAW):處理項目/子項的焦點框、高亮背景、文字繪製
  • 滑鼠訊息 (WM_LBUTTONDOWN):實作單擊高亮、雙擊進入編輯狀態
  • 標籤編輯通知 (LVN_BEGINLABELEDIT / LVN_ENDLABELEDIT):攔截編輯框建立/銷毀,重新定位至對應子項

關鍵成員變數


int m_iItem;           // 目前高亮/編輯的主項索引
int m_iSubItem;        // 目前高亮/編輯的子項索引
BOOL m_bFocus;         // 是否繪製焦點框
BOOL m_bHighLight;     // 是否高亮顯示
CItemEdit m_edtItemEdit; // 子類化編輯框控制類別

核心實作細節

1. 自訂繪製:焦點框與高亮


void CEditListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
    NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)pNMHDR;
    *pResult = CDRF_DODEFAULT;

    switch (pLVCD->nmcd.dwDrawStage) {
    case CDDS_PREPAINT:
        *pResult = CDRF_NOTIFYITEMDRAW; break;
    case CDDS_ITEMPREPAINT:
        *pResult = CDRF_NOTIFYSUBITEMDRAW; break;
    case CDDS_ITEMPREPAINT | CDDS_SUBITEM: {
        int iItem = (int)pLVCD->nmcd.dwItemSpec;
        int iSubItem = pLVCD->iSubItem;
        CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);

        CString strText = GetItemText(iItem, iSubItem);
        CRect rcItem, rcText;
        GetSubItemRect(iItem, iSubItem, LVIR_LABEL, rcItem);
        rcText = rcItem;
        rcText.left += 4;
        rcText.right = min(rcText.left + pDC->GetTextExtent(strText).cx + 6, rcItem.right);

        COLORREF crOld = pDC->GetTextColor();
        if (m_bFocus && m_iItem == iItem && m_iSubItem == iSubItem) {
            if (m_bHighLight) {
                pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
                pDC->FillSolidRect(rcText, ::GetSysColor(COLOR_HIGHLIGHT));
            }
            pDC->DrawFocusRect(rcText);
        }
        rcItem.left += 6;
        pDC->DrawText(strText, rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP);
        pDC->SetTextColor(crOld);
        *pResult = CDRF_SKIPDEFAULT;
    } break;
    }
}

2. 滑鼠點擊:高亮與編輯狀態機


void CEditListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
    m_bFocus = TRUE;
    LVHITTESTINFO hit; hit.pt = point;
    int item = SubItemHitTest(&hit);

    if (item != -1 && (hit.flags & LVHT_ONITEM)) {
        CListCtrl::OnLButtonDown(nFlags, point);
        if (m_bHighLight && m_iItem == hit.iItem && m_iSubItem == hit.iSubItem) {
            // 第二次點擊:進入編輯
            EditLabel(m_iItem);
            return;
        } else {
            // 第一次點擊:高亮
            m_iItem = hit.iItem;
            m_iSubItem = hit.iSubItem;
            m_bHighLight = TRUE;
        }
    } else {
        if (!m_edtItemEdit.m_hWnd) m_bHighLight = FALSE;
        CListCtrl::OnLButtonDown(nFlags, point);
    }
    Invalidate(); // 強制重繪
}

3. 編輯框子類化與定位


void CEditListCtrl::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_DISPINFO* pInfo = (LV_DISPINFO*)pNMHDR;
    if (m_iSubItem >= 0) {
        ASSERT(m_iItem == pInfo->item.iItem);
        CRect rcSub; GetSubItemRect(pInfo->item.iItem, m_iSubItem, LVIR_BOUNDS, rcSub);

        HWND hEdit = (HWND)SendMessage(LVM_GETEDITCONTROL);
        VERIFY(m_edtItemEdit.SubclassWindow(hEdit));
        m_edtItemEdit.m_iXPos = rcSub.left + 4;  // 文字右移 4 像素對齊
        m_edtItemEdit.SetWindowText(GetItemText(pInfo->item.iItem, m_iSubItem));
    }
    *pResult = 0;
}

void CEditListCtrl::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_DISPINFO* pInfo = (LV_DISPINFO*)pNMHDR;
    LV_ITEM* pItem = &pInfo->item;

    if (m_iSubItem >= 0 && pItem->pszText) {
        SetItemText(pItem->iItem, m_iSubItem, pItem->pszText);
    }
    VERIFY(m_edtItemEdit.UnsubclassWindow());

    // 點擊控制項外部時重置狀態
    CRect rc; GetWindowRect(&rc);
    CPoint pt; GetCursorPos(&pt);
    if (!rc.PtInRect(pt)) {
        m_iItem = m_iSubItem = -1;
        m_bFocus = m_bHighLight = FALSE;
    }
    *pResult = 0;
}

輔助訊息處理

為完善體驗,尚需處理:

  • WM_PAINT:確保編輯狀態下正確重繪
  • WM_SETFOCUS / WM_KILLFOCUS:管理焦點狀態旗標
  • WM_KEYDOWN (Esc/Enter):鍵盤取消/確認編輯

完整原始碼請參考附件專案。

執行效果

CEditListCtrl 執行畫面

總結

CEditListCtrl 以 自訂繪製 + 滑鼠狀態機 + 編輯框子類化 三大支柱,實現符合直覺的子項編輯體驗。核心技巧在於:

  1. 利用 NM_CUSTOMDRAW 繪製高亮/焦點,避免系統預設選取樣式衝突
  2. 「單擊高亮、再點編輯」模式區分選取與編輯意圖
  3. 攔截 LVN_BEGINLABELEDIT 重新定位編輯框至正確子項矩形
  4. 子類化 CEdit 控制編輯框行為(如 m_iXPos 調整文字邊距)

此類別可直接替換專案中現有 CListCtrl,無需修改資源與介面設計。

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