如何在CTreeCtrl中显示自定义工具提示?

3

我有一个继承自CTreeCtrl的类。在OnCreate()函数中,我用自定义的CToolTipCtrl对象替换了默认的对象:

int CMyTreeCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CTreeCtrl::OnCreate(lpCreateStruct) == -1)
        return -1;

    // Replace tool tip with our own which will
    // ask us for the text to display with a TTN_NEEDTEXT message
    CTooltipManager::CreateToolTip(m_pToolTip, this, AFX_TOOLTIP_TYPE_DEFAULT);
    m_pToolTip->AddTool(this, LPSTR_TEXTCALLBACK);
    SetToolTips(m_pToolTip);

    // Update: Added these two lines, which don't help either
    m_pToolTip->Activate(TRUE);
    EnableToolTips(TRUE);
    
    return 0;
}

我的消息处理程序看起来像这样:

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CMyTreeCtrl::OnTtnNeedText)

然而我从未收到过TTN_NEEDTEXT消息。我使用Spy++进行了查看,看起来这个消息确实没有被发送。

这里可能会有什么问题呢?

更新

我不确定这是否相关: CTreeCtrl的父窗口属于CDockablePane类型。 是否需要额外的工作才能使其正常工作?

5个回答

5

终于!我(部分地)解决了它:

看起来CDockablePane父窗口确实引起了这个问题...

首先,我从CTreeCtrl派生类中删除了所有特定于工具提示的代码。一切都在父窗格窗口中完成。

然后我编辑了父窗口的OnCreate()方法:

int CMyPane::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDockablePane::OnCreate(lpCreateStruct) == -1)
        return -1;

const DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
    TVS_CHECKBOXES | TVS_DISABLEDRAGDROP | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT |
    TVS_INFOTIP | TVS_NOHSCROLL | TVS_SHOWSELALWAYS;

// TREECTRL_ID is a custom member constant, set to 1
if(!m_tree.Create(dwStyle, m_treeRect, this, TREECTRL_ID ) )
{
    TRACE0("Failed to create trace tree list control.\n");
    return -1;
}

// m_pToolTip is a protected member of CDockablePane
m_pToolTip->AddTool(&m_tree, LPSTR_TEXTCALLBACK, &m_treeRect, TREECTRL_ID);
m_tree.SetToolTips(m_pToolTip);


return 0;

不幸的是,我们不能仅使用较少的参数调用AddTool(),因为如果没有设置工具ID,基类将通过ASSERT发出关于uFlag成员的投诉。由于我们需要设置ID,因此还需要设置矩形。我创建了一个CRect成员,并在CTor中将其设置为(0, 0, 10000, 10000)。我还没有找到一种有效的方法来更改工具的矩形大小,所以这是我非常丑陋的解决方案。这也是为什么我称这个解决方案为部分解决方案。更新:我提出了一个相关问题。

最后,有一个处理程序来获取工具提示信息:

// Message map entry
ON_NOTIFY(TVN_GETINFOTIP, TREECTRL_ID, &CMobileCatalogPane::OnTvnGetInfoTip)


// Handler
void CMyPane::OnTvnGetInfoTip(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMTVGETINFOTIP>(pNMHDR);

    // This is a CString member
    m_toolTipText.ReleaseBuffer();
    m_toolTipText.Empty();

    // Set your text here...

    pGetInfoTip->pszText = m_toolTipText.GetBuffer();

    *pResult = 0;
}

4

我相信即使你替换了内置的工具提示,仍然需要启用它。

EnableToolTips(TRUE);

好的,既然之前的方法对您没有起作用,而且也没有更专业的人提供帮助,那么我再给您提供一些建议。虽然它们可能不是很好,但可能会让您重新开始:

  • 确保您的OnCreate()例程实际上正在执行。
  • 在替换之前启用工具提示。
  • 我用来执行此操作的代码如下所示。(我承认我并不理解所有细节,我从一些示例代码中复制了它,它可以工作,所以我没有再看它。)

    // 启用标准工具提示

    EnableToolTips(TRUE);

    // 禁用内置工具提示

    CToolTipCtrl* pToolTipCtrl = (CToolTipCtrl*)CWnd::FromHandle((HWND)::SendMessage(m_hWnd, LVM_GETTOOLTIPS, 0, 0L));


这似乎没有任何效果。虽然我已经将其添加到代码中,还调用了CToolTipCtrl::Activate()。 - foraidt
感谢更新!不幸的是,我在这里没有进一步的了解... 我验证了OnCreate()被调用,并且在替换之前启用工具提示似乎没有任何影响。 SendMessage() 调用也没有效果。我相信这只是检索指针而并非实际禁用任何内容。 - foraidt
好吧,这很遗憾。没有人做出贡献是奇怪的。我只能建议您构建最简单的应用程序,以在测试树上显示工具提示,并查看是否有效。顺便说一下,Sendmessage()不仅检索指针。 - ravenspoint

1

你不需要重写OnToolHitTest()吗?

(旧) 资源1

(旧) 资源2:

除了返回命中代码(nHit)之外,您还需要填写TOOLINFO结构。以下是VIRGIL在CMainFrame :: OnToolHitTest中的实现方式:

 int nHit = MAKELONG(pt.x, pt.y);
 pTI->hwnd = m _ hWnd;
 pTI->uId  = nHit;
 pTI->rect = CRect(CPoint(pt.x-1,pt.y-1),CSize(2,2));
 pTI->uFlags |= TTF _ NOTBUTTON;
 pTI->lpszText = LPSTR _ TEXTCALLBACK;

大部分都很明显——比如设置hwnd和uId——但有些不太一样。我将rect成员设置为以鼠标位置为中心的2像素宽、2像素高的矩形。工具提示控件使用此矩形作为“工具”的边界矩形,我希望它很小,因此将鼠标移动到任何地方都将构成移出该工具。我在uFlags中设置了TTF_NOTBUTTON,因为工具提示与按钮无关。这是afxwin.h中定义的特殊MFC标志;MFC使用它来处理工具提示的帮助。工具提示还有另一个MFC扩展标志,即TTF_ALWAYSTIP。如果您希望MFC在窗口未处于活动状态时仍显示提示,则可以使用它。 您可能已经注意到,到目前为止,我尚未告诉MFC或工具提示或TOOLINFO提示的实际文本是什么。这就是LPSTR_TEXTCALLBACK的作用。这个特殊值告诉工具提示控件(MFC使用的内部线程全局控件)调用我的窗口以获取文本。它通过向我的窗口发送带有通知代码TTN_NEEDTEXT的WM_NOTIFY消息来实现这一点。

我不确定我做错了什么,但我仍然没有收到TTN_NEEDTEXT通知... - foraidt

1

我没有在CTreeCtrl中尝试过,但我认为您应该调用RelayEvent来让工具提示控件知道何时显示工具提示。请尝试以下操作:

MyTreeCtrl.h:

virtual BOOL PreTranslateMessage(MSG* pMsg);

MyTreeCtrl.cpp:

BOOL CMyTreeCtrl::PreTranslateMessage(MSG* pMsg) 
{
    m_pToolTip.Activate(TRUE);
    m_pToolTip.RelayEvent(pMsg);

    return CTreeCtrl::PreTranslateMessage(pMsg);
}

希望这可以帮到你。


在一个论坛上,我读到如果控件属于对话框,就必须调用RelayEvent()。然而,这个例子没有提到要调用Activate()。我尝试了你的建议,但仍然没有成功... - foraidt
很抱歉听到这个。正如我告诉你的那样,我既没有尝试过CTreeCtrl,也没有在CDockablePane中尝试过。也许如果你可以上传应用程序,我们可以看看。 - Javier De Pedro

0

尝试特别处理所有工具提示的ID:

ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CMyTreeCtrl::OnNeedTipText)

如果那不起作用,你可能需要从PreTranslateMessage()手动调用RelayEvent()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接