tags:

views:

201

answers:

1

I made a custom control derived from CWnd (a line chart) and I'm wondering if I can use CToolTipCtrl for displaying tool tips for points on the graph. If yes, how could I do that?

Btw, when I move my mouse over the point, the rectangle containg string with information about values of the point, should pop up.

+2  A: 

Yes, this works, actually I do this exact same thing, also in a line graph chart, however there are a few drawbacks/remarks. The message handling is a bit wonky, with some messages not being send according to the documentation, and some workarounds being necessary to keep the control self-contained (not requiring help from the parent to reflect notifications).

What you do is declare a variable in your CWnd-derived class

CToolTipCtrl m_ToolTipCtrl;
CString m_ToolTipContent;

Then do this on 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);

When you want to show your tooltip (presumably in OnMouseMove()), use

m_ToolTipCtrl.Popup();

BUT this only works in UNICODE builds. So if you're still on MBCS (like I am), you can only show the tooltip after a certain delay. Use this to set your tooltip text (also in 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";

Furthermore, you need to handle 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;
}

You'll have to modify this a bit, and read the documentation of the functions and messages, to get this to work in your project but yes it can be done.

Roel
@RoelI get the exception on the line `m_ToolTipCtrl.Create(this, TTS_ALWAYSTIP);`in an MyCustomCtrl::OnCreate()is there anything else besides this code I should do?It's very hard to find anything on the topic of tooltips incorporated in the custom made controls.
kobac
Hmm not sure. Try putting a ::IsWindow(m_hWnd) before the .Create(), it should be OK but maybe something is wrong. Are you calling CWnd::OnCreate in the beginning of your OnCreate()? What kind of exception are you getting? Where does the debugger break into?
Roel
Yes, I'm calling CWnd::Create() before m_ToolTipCtrl.Create(this)It breaks on`ttCtrl->Create(this, TTS_ALWAYSTIP);`with message `Unhandled exception at 0x00418c29 in TestApp.exe: 0xC0000005: Access violation reading location 0x00000000.`because it's getting NULL pointer to read.I get the same error if I exchange the call order between `CWnd::Create` and `MyCustomCtrl::Create`
kobac
Well if your ttCtrl is a pointer and not a member like I have in my example, and you don't allocate memory for it, it's not going to work... Why have you made ttCtrl a pointer and not a member?
Roel
I had a pointer. Now I've changed it to object.I had a pointer because there is an example in documentation which uses pointer, and I initialized it to NULL in constructor of CMyCustomCtrl class, and deallocated the memory in destructor.Anyway I get exception even when I use object CToolTipCtrl as a member of my MyCustomCtrl class.So my custom control is derived from CWnd.In MyCustomCtrl::Create() first I call Cwnd::Create() and then CToolTipCtrl::Create()I don't know what could be the problem, and CallStack is not showing any useful call I could rely on, trying to debug. any help?
kobac
btw in Release mode it (seems to) works okay.
kobac
When it's a member you won't get the same error message as you had first. Please re-describe the new problem you are having (because it's a different one now than before). If it works in release mode you are most likely hitting an ASSERT somewhere which the stack trace should show.
Roel
Well new problem is following.I have my class describing my custom control which is derived from `CWnd`. Btw it's a line chart control, and I would like to show tooltips on the turning points of the lines. It works fine.But when I just add an `CToolTipCtrl ttCtrl;` as a member of `MyCustomControl` class I get message `TestApp.exe has triggered a breakpoint` and this is the code I get redirected to `_CRTIMP void _cdecl _CrtDbgBreak( void ){ DebugBreak();}`Btw, I included `"afxcmn.h"` as stated in the doc and I tried with/without `ttCtrl.Create()` (after `CWnd::Create()`)
kobac
...In debug mode it doesn't start at all, but when I try it in release mode, it seems to work
kobac
Yes that's at the bottom of the stack trace, but when you go up on the stack, where does it actually go wrong. _CrtDbgBreak() is called by something, usually an ASSERT macro. Knowing that -CrtDbBreak is called doesn't help, you need to check where it is called from, and why.
Roel
it's called from dlg.DoModal() from main.cpp, but I can't figure out why it goes wrong, cause there's only system methods calls from there to the bottom of the stack.up the stack there're couple of calls that could be useful`BOOL AFXAPI AfxAssertFailedLine(LPCSTR lpszFileName, int nLine)``TestApp.exe!CTestAppDlg::GetMessageMap() Line 43 + 0x28 bytes C++``TestApp.exe!CTestAppApp::InitInstance() Line 63 + 0xb bytes C++`I think it's related to initializing of custom control cause, just when I add CToolTipCtrl as a member it fails.
kobac
Check the method signatures - look up the messages in your message map and check their types against what you have in your control's header. It looks like your control is receiving messages for which the handler prototype doesn't match the message's parameters. The parameters are in MSDN.
Roel