1、引言
HitTest是命中测试的意思。在使用MFC编程的时候,在列表视图中通常会涉及到需要获取鼠标当前位置所对应的元素。这种通过光标位置来获得相应元素项的操作我们称之为命中测试。
2、获取光标位置
网上有人这样处理:
DWORDdwPos = ::GetMessagePos(); //获得包含坐标信息的DWORD类型
CPointpt(LOWORD(dwPos), HIWORD(dwPos)); //将坐标值转换为CPoint类型
ScreenToClient(&pt); //将屏幕坐标转换为窗口客户区坐标
考虑到兼容性、可移植性等不建议这样处理,原因如下:
GetMessagePos
函数原型 DWORD GetMessagePos(VOID)
正确处理方式如下:
CPoint point;
GetCursorPos(&point);
3、在ListControl中获取当前选中项
资源视图-->选中ListControl控件-->属性中点击闪电符号-->添加NM_CLICK(即鼠标通知消息),在响应函数OnNMClick****(NMHDR *pNMHDR, LRESULT *pResult)中添加如下代码:
//获取当前光标位置
CPoint point;
GetCursorPos(&point);
//将屏幕坐标转到客户区坐标
m_list.ScreenToClient(&point); (注意:CListControl m_list)
或者
ScreenToClient(this,&point);也可以实现屏幕坐标转客户区坐标
参考:
ScreenToClient
顾名思义:ScreenToClient也就是Screen(屏幕坐标) 到 Client(客户区坐标)的转换。也就是说这个函数可以把你在屏幕上鼠标的位置转换为你打开的程序的客户区的坐标(位置)。hWnd
指向窗口的句柄,此窗口的用户空间将被用来转换。
lpPoint
指向POINT结构指针,该结构含有要转换的屏幕坐标。
返回值
如果函数调用成功,返回值为非零值,否则为零。
LVHITTESTINFO
typedef struct _LVHITTESTINFO { POINT pt; //输入 UINT flags; //输出 int iItem;//输出 int iSubItem;//输出 int iGroup;//输出 } LVHITTESTINFO, *LPLVHITTESTINFO; 上面这个结构体给定pt值后,在应用命中测试(hittest)之后,其他三个参数就可以获取到数据。 LVHITTESTINFO info; info.pt = point; int Itsub = m_list.SubItemHitTest(&info); int row = info.iItem;//行号从0开始 int col = info.iSubItem;//列号从0开始 UINT UtEST = info.flags;//一般点击在可编辑的列上返回#define LVHT_ONITEMLABEL 0x00000004,点击到控件上的其他地方返回#define LVHT_NOWHERE 0x00000001 参考:C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include\CommCtrl.h #define LVHT_NOWHERE 0x00000001 #define LVHT_ONITEMICON 0x00000002 #define LVHT_ONITEMLABEL 0x00000004 #define LVHT_ONITEMSTATEICON 0x00000008 #define LVHT_ONITEM (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON) #define LVHT_ABOVE 0x00000008 #define LVHT_BELOW 0x00000010 #define LVHT_TORIGHT 0x00000020 #define LVHT_TOLEFT 0x00000040 #define LVHT_EX_GROUP_HEADER 0x10000000 #define LVHT_EX_GROUP_FOOTER 0x20000000 #define LVHT_EX_GROUP_COLLAPSE 0x40000000 #define LVHT_EX_GROUP_BACKGROUND 0x80000000 #define LVHT_EX_GROUP_STATEICON 0x01000000 #define LVHT_EX_GROUP_SUBSETLINK 0x02000000 #define LVHT_EX_GROUP (LVHT_EX_GROUP_BACKGROUND | LVHT_EX_GROUP_COLLAPSE | LVHT_EX_GROUP_FOOTER | LVHT_EX_GROUP_HEADER | LVHT_EX_GROUP_STATEICON | LVHT_EX_GROUP_SUBSETLINK) #define LVHT_EX_ONCONTENTS 0x04000000 // On item AND not on the background #define LVHT_EX_FOOTER 0x08000000 参考:https://msdn.microsoft.com/zh-cn/vstudio/bb774754关于该结构体的定义
其他相关:CListControl类中有下面三个函数
// Determines the visual feature of a subitem control under
// the specified point.
int SubItemHitTest(_In_ LPLVHITTESTINFO pInfo);
// Determines the visual feature of the control under
// the specified point.
int HitTest(_In_ LVHITTESTINFO* pHitTestInfo) const;
int HitTest(_In_ CPoint pt, _In_opt_ UINT* pFlags = NULL) const;
注意体会区别,使用HitTest无法获取iSubItem的值,只能获得iItem的值,使用SubItemHitTest就可以获得iItem和iSubItem的值。
总结:一般常用的如下
void C*****Dlg::OnNMClick***(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
CPoint point;
GetCursorPos(&point);
//将屏幕坐标转到客户区坐标
m_list.ScreenToClient(&point); (Glg.h文件中定义 CListCtrl m_list;)
LVHITTESTINFO info;
info.pt = point;
int Itsub = m_list.SubItemHitTest(&info);
int row = info.iItem;//行号从0开始
int col = info.iSubItem;//列号从0开始
}
MSDN给出的例子如下:
void CListCtrlDlg::OnDblClk(NMHDR* pNMHDR, LRESULT* pResult) { UNREFERENCED_PARAMETER(pResult); LPNMITEMACTIVATE pia = (LPNMITEMACTIVATE)pNMHDR; LVHITTESTINFO lvhti; // Clear the subitem text the user clicked on. lvhti.pt = pia->ptAction; m_myListCtrl.SubItemHitTest(&lvhti); if (lvhti.flags & LVHT_ONITEMLABEL) { m_myListCtrl.SetItemText(lvhti.iItem, lvhti.iSubItem, L"要输入的内容");
详细代码见: