使用CToolTipCtrl为自定义控件添加工具提示?(MFC)

3
我创建了一个从CWnd派生的自定义控件(折线图),我想知道是否可以使用CToolTipCtrl来显示图上点的工具提示。如果可以,如何实现呢?
另外,当我将鼠标移动到该点上时,应弹出包含有关该点值信息的字符串的矩形框。
2个回答

5

是的,这个方法是有效的,实际上我也在使用这种方法,在折线图表中同样适用,但是还有一些缺点/注意事项。消息处理有点不稳定,有些消息不按照文档发送,需要一些解决方法来保持控件的自包含性(不需要父级帮助反映通知)。

你需要在你的CWnd派生类中声明一个变量。

CToolTipCtrl m_ToolTipCtrl;
CString m_ToolTipContent;

在OnCreate中执行以下操作:

m_ToolTipCtrl.Create(this, TTS_ALWAYSTIP);
m_ToolTipCtrl.Activate(TRUE);

你还可以选择设置延迟时间:

m_ToolTipCtrl.SetDelayTime(TTDT_AUTOPOP, -1);
m_ToolTipCtrl.SetDelayTime(TTDT_INITIAL, 0);
m_ToolTipCtrl.SetDelayTime(TTDT_RESHOW, 0);

当您想要显示工具提示(可能在OnMouseMove()中),请使用:
m_ToolTipCtrl.Pop();

但是这仅适用于UNICODE版本。所以如果您仍在使用MBCS(就像我一样),您只能在一定的延迟后显示工具提示。 在OnMouseMove中使用以下代码设置您的工具提示文本:

// Not using CToolTipCtrl::AddTool() because
// it redirects the messages to the parent
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND;    // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd;   // Handle to the control
ti.hwnd = m_hWnd;            // Handle to window
// to receive the tooltip-messages
ti.hinst = ::AfxGetInstanceHandle();
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.rect = <rectangle where, when the mouse is over it, the tooltip should be shown>;
m_ToolTipCtrl.SendMessage(TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
m_ToolTipCtrl.Activate(TRUE);

m_ToolTipContent = "my tooltip content";

此外,您需要处理TTNNeedText:
// The build-agnostic one doesn't work for some reason.
ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnTTNNeedText)
ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnTTNNeedText)

BOOL GraphCtrlOnTTNNeedText(UINT id, NMHDR* pTTTStruct,  LRESULT* pResult)
{
    TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*)pTTTStruct;
    //pTTT->lpszText = "some test text";
    //pTTT->lpszText = m_ToolTipContent;
    strncpy_s(pTTT->lpszText, 80, m_ToolTipContent, _TRUNCATE);

    return TRUE;
}

您需要稍微修改一下,并阅读函数和消息的文档,才能在您的项目中使其正常运行,但是它确实可以完成。

我在MyCustomCtrl::OnCreate()中的这行代码m_ToolTipCtrl.Create(this, TTS_ALWAYSTIP);上遇到了异常,除了这段代码之外还有其他应该做的吗?很难找到关于自定义控件中嵌入工具提示的资料。 - dragan.stepanovic
嗯,不太确定。试着在.Create()之前加上::IsWindow(m_hWnd)吧,应该没问题但可能哪里出错了。你是否在OnCreate()的开头调用了CWnd::OnCreate()? 你遇到了什么样的异常?调试器断在哪里了? - Roel
是的,在m_ToolTipCtrl.Create(this)之前我正在调用CWnd::Create()。 它会在ttCtrl->Create(this, TTS_ALWAYSTIP);处中断, 并显示以下消息:Unhandled exception at 0x00418c29 in TestApp.exe: 0xC0000005: Access violation reading location 0x00000000.,因为它获取了空指针来读取。 如果我交换CWnd::Create和MyCustomCtrl::Create之间的调用顺序,我会得到相同的错误。 - dragan.stepanovic
我之前使用了一个指针,现在我已经将其更改为对象。 我使用指针的原因是文档中有一个使用指针的示例,并且我在CMyCustomCtrl类的构造函数中将其初始化为NULL,在析构函数中释放了内存。无论如何,即使我将CToolTipCtrl作为MyCustomCtrl类的成员使用,我仍然会遇到异常情况。 因此,我的自定义控件是从CWnd派生而来的。 在MyCustomCtrl :: Create()中,我首先调用Cwnd :: Create(),然后调用CToolTipCtrl :: Create()。 我不知道问题可能是什么,而且CallStack没有显示任何有用的调用,我可以依赖于尝试进行调试。 有什么帮助吗? - dragan.stepanovic
当它是成员时,您将不会收到与最初相同的错误消息。请重新描述您遇到的新问题(因为现在与之前不同)。如果在发布模式下运行正常,则很可能会触发某个断言,堆栈跟踪应该显示出来。 - Roel
显示剩余5条评论

0
对于那些仍在寻找答案的人,就像我一样。(我无法让上面的答案起作用 - MFC中的事情可能已经改变了。)
所有代码都包含在自定义控件类中。
在您的类定义中添加:
CToolTipCtrl ToolTip;

在 PreSubclassWindow() 中添加:
#define TOOLTIP_ID 1
ToolTip.Create(this, TTS_ALWAYSTIP );

CRect rc;
GetClientRect(rc);
ToolTip.AddTool(this, "Tool tip text", rc, TOOLTIP_ID);

在 PreTranslateMessage() 中添加:
ToolTip.RelayEvent(msg);

每当您的工具提示文本更改时,请添加以下内容:
ToolTip.UpdateTipText("New Tip Text", this, TOOLTIP_ID);

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