编辑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; } } 
|