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