如何保证我获得用户在 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::OnRButtonDblClkCWnd::OnRButtonDownCWnd::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 记录而不重叠?

我如何在 discord.js 中获得提及用户的用户名

如何在 WKWebView 中注入多个用户脚本以获得暗模式效果?

如何获得保证的无效迭代器(用于向量)?

如何在 Petgraph 中获得确定性拓扑排序?

如何在 .NET 中获得绝对或规范化的文件路径?