如何保证我获得用户在 CTreeControl 中右键单击的项目的“HTREEITEM”
Posted
技术标签:
【中文标题】如何保证我获得用户在 CTreeControl 中右键单击的项目的“HTREEITEM”【英文标题】:How to guarantee that I get the `HTREEITEM` for the item that the user right-clicked on in a CTreeControl 【发布时间】:2020-08-29 16:33:55 【问题描述】:我有一个带有CTreeCtrl
的窗口。用户可以右键单击任何元素并显示上下文菜单。他们可以从那里选择删除条目。像这样的:
这是上下文菜单项处理程序的 sn-p:
void CAssignHistoryDlg::OnDeleteFromAssignmentHistory()
CString strINI = theApp.GetAssignHistoryPath();
HTREEITEM hItem = m_treeHistory.GetSelectedItem();
CString strName, strDeletedName, strEntry;
if (m_treeHistory.GetParentItem(hItem) != nullptr)
// The user has picked one of the history dates.
// So the parent should be the actual name.
hItem = m_treeHistory.GetParentItem(hItem);
// Now OK to proceed
strName = ExtractName(hItem);
GetParent()->EnableWindow(FALSE);
strEntry.Format(IDS_TPL_SURE_DELETE_FROM_ASSIGN_HIST, strName);
if (AfxMessageBox(strEntry, MB_YESNO | MB_ICONQUESTION) == IDNO)
图片显示了我的问题。如果我首先点击测试,使它被选中和亮蓝色,然后右键点击,它在弹出消息中显示 Test。这可以。但是……
如果名字是最初选择的,我继续直接右键单击测试,即使它看起来是蓝色的(就像被选中一样),m_treeHistory.GetSelectedItem()
正在返回原始的名字。我觉得我描述得不太好。
简而言之,我想保证我得到用户右键单击的项目的HTREEITEM
。我所拥有的并非 100% 万无一失。
如果有帮助,这就是我显示上下文菜单的方式:
void CAssignHistoryDlg::OnNMRclickTreeHistory(NMHDR *pNMHDR, LRESULT *pResult)
CMenu mnuContext, *pMnuEdit = nullptr;
CPoint ptLocal;
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
GetCursorPos(&ptLocal);
mnuContext.LoadMenu( IDR_MENU_SM_ASSIGN_HIST_POPUP );
pMnuEdit = mnuContext.GetSubMenu( 0 );
if (pMnuEdit != nullptr)
pMnuEdit->TrackPopupMenu( TPM_LEFTALIGN|TPM_LEFTBUTTON,
ptLocal.x, ptLocal.y, this, nullptr );
*pResult = 0;
所以回顾一下,目前用户必须在树中的一个项目上单击鼠标左键才能选择它。然后他们可以右键单击,它将提供删除此人。但是,如果他们继续并只是右键单击任何人,则不会删除该人。
【问题讨论】:
我找到了这篇文章并正在阅读它。 codeguru.com/cpp/controls/treeview/misc-advanced/article.php/… 【参考方案1】:您可以使用CTreeCtrl
类的HitTest()
成员在任何给定点获取实际 树项。您执行此操作的具体方式和位置将取决于您的代码设计,但是,如果您有一个指向 CTreeCtrl
(下面代码中的 pwTree
)的指针,那么您可以执行以下操作:
CPoint ptLocal;
GetCursorPos(&ptLocal);
pWTree->ScreenToClient(&ptLocal); // May not be required in certain handlers?
HTREEITEM hItem = pWTree->HitTest(ptLocal); // Remember to check for NULL return!
然后,您可以直接使用返回的 hItem
,或使用它显式设置树的选择(针对该项目),然后再进行任何进一步的处理。
【讨论】:
谢谢。我让它工作。但是,我不得不将OnNMRclickTreeHistory
处理程序中的CPoint
缓存到一个变量中。然后,按照您的指示使用它。这是因为菜单项本身向下和向右偏移,所以HitTest
响应是错误的项。通过在右键单击处理程序中缓存该点并随后使用它,它总是会产生正确的项目!再次感谢。 :)【参考方案2】:
MS 文档:https://docs.microsoft.com/en-us/cpp/mfc/reference/cwnd-class?view=vs-2019 没有“点击”处理程序,它有 CWnd::OnRButtonDblClk
、CWnd::OnRButtonDown
和 CWnd::OnRButtonUp
。你在处理哪一个?
您的缺陷的原因可能是您没有让树控件处理该右键事件(以选择该新项目)。
我建议改用CWnd::OnContextMenu
。
【讨论】:
嗨。接受的答案是解决方案。我使用 NM_RCLICK。见docs.microsoft.com/en-us/windows/win32/controls/… @AndrewTruckle - 即使这解决了您的问题,也不是解决方案;充其量是kludge。您正在删除在调用OnDeleteFromAssignmentHistory
时光标指向的项目,该项目可能是也可能不是在调用菜单时选择的正常项目。我会尝试弄清楚为什么预期的选择与实际的选择不匹配,我认为我的建议会真的解决这个问题。
@AndrewTruckle - 我做了一些测试,似乎右键单击本身不会改变 CTreeCtrl 中的选择;您可以找到光标下的项目并调用CTreeCtrl::SelectItem()
。我真的相信这是处理选择的正确方法。
例如:jeffpar.github.io/kbarchive/kb/222/Q222905
你真的读过知识库文章吗?它使用我正在使用的处理程序,并获取该处理程序中的鼠标位置。他们使用该信息模拟内容菜单处理程序并发布消息。然后使用HitTest
。这不是正常情况。这是CTreeCtrl
,它不一样。感谢您的回答,但指导很明确。我们可以把它留在那里吗?谢谢。以上是关于如何保证我获得用户在 CTreeControl 中右键单击的项目的“HTREEITEM”的主要内容,如果未能解决你的问题,请参考以下文章
如何让多个用户同时更新我的一条 CloudKit 记录而不重叠?