List Control就地编辑验证:点击滚动条时用旧文本验证的实现
解决List Control就地编辑点击滚动条恢复旧文本的问题
我之前也碰到过一模一样的需求,折腾了好一会儿才找到合适的解决办法,给你分享几个亲测有效的方案:
方案一:拦截滚动条消息,主动触发ESC取消逻辑
这个思路最直接——当用户点击滚动条时,我们提前让编辑控件执行和按下ESC一样的取消操作,不让它走到KillFocus的验证逻辑里。
- 给你的CListCtrl派生类添加滚动条消息处理
在派生类的消息映射中添加:
然后实现对应的处理函数:ON_WM_HSCROLL() ON_WM_VSCROLL()
这样用户一点滚动条,编辑控件就会先取消编辑、恢复旧文本,再执行滚动操作,完美符合你的需求。void CMyListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // 检查当前是否有激活的就地编辑控件 CWnd* pEdit = GetEditControl(); if (pEdit && pEdit->GetSafeHwnd()) { // 发送ESC按键消息,模拟取消编辑 pEdit->SendMessage(WM_KEYDOWN, VK_ESCAPE, 0); pEdit->SendMessage(WM_KEYUP, VK_ESCAPE, 0); } // 别忘了调用基类的处理函数 CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar); } void CMyListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { CWnd* pEdit = GetEditControl(); if (pEdit && pEdit->GetSafeHwnd()) { pEdit->SendMessage(WM_KEYDOWN, VK_ESCAPE, 0); pEdit->SendMessage(WM_KEYUP, VK_ESCAPE, 0); } CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar); }
方案二:在编辑控件的KillFocus中判断焦点目标
如果不想动List Control的滚动条处理,也可以在编辑控件的失去焦点事件里做文章——判断焦点是不是转移到了滚动条上,如果是,就用旧文本恢复。
先记录编辑前的旧文本
当开始就地编辑时(响应LVN_BEGINLABELEDIT消息),把当前项的原始文本存下来,比如存在List Control的成员变量m_strOldText里:void CMyListCtrl::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult) { NMLVDISPINFO* pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR); // 保存编辑前的旧文本 m_strOldText = pDispInfo->item.pszText; *pResult = 0; }在自定义CEdit的OnKillFocus里做判断
假设你用了自定义的CEdit派生类作为就地编辑控件,在它的OnKillFocus方法里:void CMyEdit::OnKillFocus(CWnd* pNewWnd) { CMyListCtrl* pList = dynamic_cast<CMyListCtrl*>(GetParent()); if (pList) { // 判断新焦点窗口是不是滚动条 TCHAR szClass[256]; if (pNewWnd && GetClassName(pNewWnd->GetSafeHwnd(), szClass, 256)) { if (_tcscmp(szClass, _T("SCROLLBAR")) == 0) { // 恢复旧文本并取消编辑 SetWindowText(pList->m_strOldText); pList->EndLabelEdit(FALSE); // FALSE表示放弃编辑 return; } } } // 不是滚动条的情况,正常执行基类的失去焦点逻辑 CEdit::OnKillFocus(pNewWnd); }这里通过窗口类名判断是不是滚动条,比用控件ID更可靠,因为List Control的滚动条是它的子窗口,没有固定的ID。
方案三:在CEdit的PreTranslateMessage里提前拦截鼠标点击
这个方案是在编辑控件处理鼠标消息时,提前判断用户是不是点击了滚动条,如果是就直接触发取消编辑:
BOOL CMyEdit::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_LBUTTONDOWN) { CMyListCtrl* pList = dynamic_cast<CMyListCtrl*>(GetParent()); if (pList) { // 获取点击位置对应的窗口 CWnd* pClickedWnd = WindowFromPoint(pMsg->pt); TCHAR szClass[256]; if (pClickedWnd && GetClassName(pClickedWnd->GetSafeHwnd(), szClass, 256)) { if (_tcscmp(szClass, _T("SCROLLBAR")) == 0) { // 发送ESC消息取消编辑 SendMessage(WM_KEYDOWN, VK_ESCAPE, 0); SendMessage(WM_KEYUP, VK_ESCAPE, 0); return TRUE; // 拦截这个鼠标消息,不让它继续处理 } } } } return CEdit::PreTranslateMessage(pMsg); }
一些注意点
- 不管用哪个方案,一定要在编辑开始时正确记录旧文本,否则恢复的时候没有依据。
- 如果用的是MFC默认的就地编辑(没有自定义CEdit),可以通过
GetEditControl()获取到默认的编辑控件,然后给它子类化,再添加上面的逻辑。
内容的提问来源于stack exchange,提问作者user9444860
相关产品推荐
相关产品推荐

