编辑ListViewSubItem的一种另的方法

类别:VC语言 点击:0 评论:0 推荐:

编辑ListView Ctrl任一SubItem的一种另类的方法,欢迎大家讨论,一起来找出更简单的办法,没用MFC,用VB的朋友也可以来讨论思路。

在ListView控件中,有一个EditLabel选项,如果仅仅只要编辑第一Column(也就是Label)的内容,那是非常容易的,这里就不讨论了。编辑每一个SubItem在MSDN也好像也没有讲(我自己还没有找到资料), 在CodeGuru上面这方面讲的比较多,我找了一下,不过都是用MFC做的,感觉代码很多,我没有非常仔细的看下去。
当然,实现此项功能一般也不会用到,所以高手们也没有顾及到。不过我却曾经应聘碰到过考官出这样的题目,当时一听我就懵了,搞得后面的回答也一塌糊涂,把一张老脸给丢了个精光,这是题外话。


下面来写写我的思路


我个人的考虑是,首先判断在ListCtrl选定的是哪个SubItem,只要获得了这个SubItem的iItem和iSubItem,其他的问题就会很好解决(在VB中,此项应该更加容易)。 既然得知了是哪个SubItem被选择,那么就可以创建一个Edit类型的子窗口,把这个子窗口放在SubItem的位置,子窗口失去焦点后就将这个Edit的内容来修改SubItem的内容就搞定了。


首先是获取SubItem的位置,即它的行和列。如果将这个ListCtrl的风格设置为LVS_EX_FULLROWSELECT的话,点击使用MFC的GetNextItem(-1, LVNI_SELECTED)函数或者使用GetFirstSelectedItem()和GetNextSelectItem()就可以获得,问题是,如果没有将其风格设置为LVS_EX_FULLROWSELECT的时候,使用GetNextItem(-1, LVNI_SELECTED)在没有点击Label(也就是第一个Column)的时候,是不能返回其iItem的。所以么另想办法。
可以这样:在点击ListCtrl时,控件会向其父窗口发送WM_NOTIFY消息,在此消息中,包含了我们需要的信息。
在响应WM_NOTIFY消息的时候,转换其参数lParam为NMHDR结构,其code字段如果为NM_CLICK就可以判断出是点击了ListCtrl控件。再将其转换为NMLISTVIEW结构,就可以获取我们需要iItem, iSubItem等字段。以下是模仿
MSDN的写法:
 switch (((LPNMHDR) lParam)->code) {
  case NM_CLICK:
  #define pnm ((LPNMLISTVIEW)lParam)
  POINT pt;
  int iSubItem;
  pt = pnm->ptAction;
  iSubItem = pnm->iSubItem;
  #undef pnm
               ……………………


现在获得了SubItem的位置资料,发送消息::SendMessage(HWND, LVM_GETSUBITEMRECT, WPARAM, LPARAM)就可以获得SubItem处的矩形了。
获得了SubItem处的矩形,然后调用CreateWindow来创建Edit子窗口就非常容易了。具体请看源代码
(Demo Project和其完整源代码在此下载)
BOOL CEditListCtrlDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
 // TODO: Add your specialized code here and/or call the base class
 switch (((LPNMHDR) lParam)->code) {
  case NM_CLICK:
   
  #define pnm ((LPNMLISTVIEW)lParam)
  POINT pt;
  RECT rect;
  int iHeight;
  int s_iItem;
  int s_iSubItem;


  pt = pnm->ptAction;
  
  s_iSubItem = pnm->iSubItem;
  ListView_GetSubItemRect(m_List.m_hWnd, 0, s_iSubItem, LVIR_BOUNDS, &rect);
  #undef pnm
  iHeight = rect.bottom - rect.top;
  s_iItem = floor(pt.y / iHeight) - 1;
  if (bClick)  file://判断是否是第二次点击同一个SubItem  
  {
   if (s_iItem != iItem || s_iSubItem != iSubItem)
   {
    iItem = s_iItem;
    iSubItem = s_iSubItem;
    break;
   }
  }
  else
  {
   bClick = true;
   iItem = s_iItem;
   iSubItem = s_iSubItem;
   break;
  }


  if (iItem >= ::SendMessage(m_List.m_hWnd, LVM_GETITEMCOUNT, 0, 0))
   break;          file://如果点击处没有Item,这退出
  ListView_GetSubItemRect(m_List.m_hWnd, iItem, iSubItem, LVIR_LABEL, &rect);     file://得到SubItem的矩形
  bCreateEdit(rect, iItem, iSubItem);
  
  break;
 }
 return CDialog::OnNotify(wParam, lParam, pResult);
}


/**********************************************************************
bCreateEdit函数在ListView Ctrl的SubItem位置创建一个Edit的子窗口,用这个子窗口来取得对
SubItem文本的改变,如果创建成功则返回true,如果失败,则返回false
rect为一大小和位置都与SubItem对应的矩形,iItem为当前选择的Item,iSubItem为当前选择SubItem
**********************************************************************/
bool CEditListCtrlDlg::bCreateEdit(const RECT & rect, int iItem, int iSubItem)
{
 
 int iLeft;
 int iTextWidth;
 
 char *  lpszText = new char[100];
 ListView_GetItemText(m_List.m_hWnd, iItem, iSubItem, lpszText, 100); file://get subitem text
 
 GetLeft_Width(iLeft, iTextWidth, strlen(lpszText), rect);       file://确定创建的窗口的位置和宽度 
 if (iSubItem == 0)
  iLeft = rect.left + 2;


 ::Sleep(300);
 hwndEdit = CreateWindow(_T("edit"), NULL,\
 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT, iLeft, rect.top - 1, iTextWidth + 5, \
 (rect.bottom - rect.top + 3),\
 m_List.m_hWnd, (HMENU)1313, (HINSTANCE)GetWindowLong(this->m_hWnd, GWL_HINSTANCE), NULL); file://创建窗口
 if (hwndEdit == NULL)
 {
  delete [] lpszText;
  return false;
 }
 
 HFONT listFont = (HFONT)::SendMessage(m_List.m_hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0);
 ::SendMessage(hwndEdit, WM_SETFONT, (WPARAM)listFont, MAKELPARAM(FALSE, 0));
 ::SetWindowText(hwndEdit, lpszText);       file://设置创建的窗口的字体和初始的文本为SubItem的文本
 delete [] lpszText;
 ::SetFocus(hwndEdit);
 ::SendMessage(hwndEdit, EM_SETSEL, (WPARAM)(INT)0, (LPARAM)(INT)-1); file://窗口的文本全部选定,类似于系统的风格
 
 return true;


}


/*****************************************************************************************
函数GetLeft_Width()为将要创建的Edit子窗口所在的位置和宽度,它要根据SubItem的长度来确定
iLeft为子窗口所在的位置的Left的应用,iTextWidth为SubItem的字符串的逻辑长度,rect为对应
SubItem的矩形,iLenStr为SubItem的字符个数
******************************************************************************************/
void CEditListCtrlDlg :: GetLeft_Width(int & iLeft, int & iTextWidth, int iLenStr, const RECT & rect)
{
 TEXTMETRIC tm;
 int iRectWidth; 
 iRectWidth = rect.right - rect.left;
 
 HDC hdc = ::GetWindowDC(m_List.m_hWnd);
 ::GetTextMetrics(hdc, &tm);
 ::ReleaseDC(m_List.m_hWnd, hdc);
 iTextWidth = tm.tmAveCharWidth * iLenStr;
 if (iRectWidth > iTextWidth)
  iLeft = rect.left + (iRectWidth - iTextWidth) / 2 + 1;
 else
  iLeft = rect.left + 2;
 if (iLenStr == 0)
 {
  iLeft = rect.left + iRectWidth / 4;
  iTextWidth = iRectWidth / 2;
 }
}

本文地址:http://com.8s8s.com/it/it2622.htm