程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 可編輯子項的CListCtrl類

可編輯子項的CListCtrl類

編輯:關於VC++

一、說明

大家都知道在MFC中通過給CListCtrl設置LVS_EDITLABELS屬性,並且在程序中響應控件的LVN_ENDLABELEDIT消息可以修改列表控件每一行的第一項,也就是主項(Item)。代碼如下:

void CEditListCtrlSampleDlg::OnEndlabeleditList1(NMHDR* pNMHDR, LRESULT* pResult)
{
  LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
  // TODO: Add your control notification handler code here

  *pResult = TRUE;  //TRUE值表示可以修改主項,FALSE值表示不修改主項
}

但是讓人郁悶的是,微軟留了一手,CListCtrl不支持直接修改子項(SubItem)。無奈之下只好自力更生,對CListCtrl進行擴展。>_<!!!!

二、原理

通過在浩如煙海的互聯網上查找資料(當然包括了大名鼎鼎的VCKBASE),發現現有的實現大都是對子項鼠標單擊一次就可以編輯。但本人對CListCtrl的單擊一次高亮文本,再單擊一次才開始編輯的操作模式感覺比較喜歡,所以就有了這篇文章的誕生。

要想實現高亮文本也就是對文本進行著色處理,這可以通過對NM_CUSTOMDRAW消息進行處理實現,但是類向導中沒有這個消息映射只能進行手工添加。

要想編輯文本則可以通過EditLabel(int nItem)成員函數以及對LVN_BEGINLABELEDIT和LVN_ENDLABELEDIT的消息處理實現。

三、實現

本文最終實現的CEditListCtrl擴展類在盡量符合CListCtrl操作步驟的情況下實現對主項及子項的可編輯。

成員變量說明:

int m_iItem; //主項標識符

int m_iSubItem; //子項標識符

BOOL m_bFocus; //是否繪制項文本焦點框

BOOL m_bHighLight; //是否高亮項文本

CItemEdit m_edtItemEdit; //用於子類化EditLabel函數返回的CEdit*指針

列表控件中所有項文本的繪制以及特效(焦點框、高亮)都在NM_CUSTOMDRAW消息處理中實現:

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

  // Take the default processing unless we set this to something else below.
  *pResult = CDRF_DODEFAULT;

  // First thing - check the draw stage. If it's the control's prepaint
  // stage, then tell Windows we want messages for every item.

  if (pNMLVCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)
  {
    *pResult = CDRF_NOTIFYITEMDRAW;
  }
  else if (pNMLVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
  {
    // This is the notification message for an item. We'll request
    // notifications before each subitem's prepaint stage.
    *pResult = CDRF_NOTIFYSUBITEMDRAW;
  }
  else if (pNMLVCustomDraw->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))
  {
    //當前要繪制的主項標識符和子項標識符
    int iItem = (int)pNMLVCustomDraw->nmcd.dwItemSpec;
    int iSubItem = pNMLVCustomDraw->iSubItem;

    CDC* pDC = CDC::FromHandle(pNMLVCustomDraw->nmcd.hdc);

    CString strItemText = GetItemText(iItem, iSubItem);
    CRect rcItem, rcText;
    GetSubItemRect(iItem, iSubItem, LVIR_LABEL, rcItem);
    rcText = rcItem;

    CSize size = pDC->GetTextExtent(strItemText);
    if(strItemText == _T(""))
    {
      size.cx = 41;
    }

    //設置文本高亮矩形
    rcText.left += 4;
    rcText.right = rcText.left + size.cx + 6;
      if(rcText.right > rcItem.right)
  {
    rcText.right = rcItem.right;
  }

  COLORREF crOldTextColor = pDC->GetTextColor();
  //繪制項焦點/高亮效果
  if(m_bFocus)
  {
    if((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(strItemText, &rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP);
  pDC->SetTextColor(crOldTextColor);
  *pResult = CDRF_SKIPDEFAULT;// We've painted everything.
  }
}

 

單擊一次文本高亮,再單擊一次文本開始編輯在WM_LBUTTONDOWN消息處理中實現:

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

  //if (over a item/subitem)
  if (item != -1 && (lvhit.flags & LVHT_ONITEM))
  {
    CListCtrl::OnLButtonDown(nFlags, point);

    if(m_bHighLight && m_iItem == lvhit.iItem && m_iSubItem == lvhit.iSubItem)
    {
      //第二次單擊
      EditLabel(m_iItem);
      return;
    }
    else
    {
      //第一次單擊
      m_iItem = lvhit.iItem;
      m_iSubItem = lvhit.iSubItem;
      m_bHighLight = TRUE;
    }
  }
  else
  {
    if(m_edtItemEdit.m_hWnd == NULL)
    {
      //未出現文本編輯框時
      m_bHighLight = FALSE;
    }

    CListCtrl::OnLButtonDown(nFlags, point);
  }

  Invalidate(); //強制重繪控件
}

關鍵的一步,對項文本進行編輯。在以上代碼中當執行到EditLabel時將會產生一個編輯框,這時需要將它進行子類化處理,以控制它出現的位置。

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

    //get edit control and subclass
    HWND hWnd= (HWND)SendMessage(LVM_GETEDITCONTROL);
    ASSERT(hWnd != NULL);
    VERIFY(m_edtItemEdit.SubclassWindow(hWnd));
    //move edit control text 4 pixel to the right of org label,
    //as Windows does it...編輯框定位
    m_edtItemEdit.m_iXPos = rcSubItem.left + 4;
    m_edtItemEdit.SetWindowText(GetItemText(pDispInfo->item.iItem, m_iSubItem));
  }
  *pResult = 0;
}
void CEditListCtrl::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
{
  LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
  LV_ITEM  *plvItem = &pDispInfo->item;

  if (m_iSubItem >= 0)
  {
    if (plvItem->pszText != NULL )
    {
      SetItemText(plvItem->iItem,m_iSubItem, plvItem->pszText);
    }

    VERIFY(m_edtItemEdit.UnsubclassWindow()!=NULL);
    *pResult = 0;
  }
  //編輯文本時對控件父窗口操作(如單擊其它控件)引發"OnEndlabeledit"時刷新控件
  CRect rect;
  GetWindowRect(&rect);
  CPoint point;
  ::GetCursorPos(&point);
  if(!rect.PtInRect(point))
  {
    m_iItem = -1;
    m_iSubItem = -1;
    m_bFocus = FALSE;
    m_bHighLight = FALSE;
  }
}

通過以上三個步驟大體實現了本文要達到的目的,但是還不能放松。接下來還要進行一些顯示細節方面的處理。這包括對WM_PAINT、WM_SETFOCUS和WM_KILLFOCUS消息的處理,限於篇幅,就不進行細講了,有興趣的朋友可以查看本文提供的源代碼。最後實現的效果如下圖所示:

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved