How to set tooltip at runtime in MFC Treeview? - c++

How to set tooltip at runtime in MFC Treeview ?
I am creating treeview like this :
m_pTreeview->Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP |
TVS_SINGLEEXPAND,CRect(38, 82, 220 ,250), this, IDC_NDS_TREEVIEW);
Any help is appreciated..

Here some code : -- In .H file
afx_msg void OnMyTreeGetInfoTip(NMHDR pNMHDR, LRESULT pResult);
In BEGIN MESSAGE MAP block add -
ON_NOTIFY_REFLECT (TVN_GETINFOTIP, OnMyTreeGetInfoTip)
And use handler
void CMyTreeView::OnMyTreeGetInfoTip(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTVGETINFOTIP pGetInfoTip = (LPNMTVGETINFOTIP)pNMHDR;
CString strItemTxt = m_TreeCtrl.GetItemText(pGetInfoTip->hItem);
strcpy(pGetInfoTip->pszText, strItemTxt);
*pResult = 0;
}

If you're referring to the tooltips for items in the tree control, you need to add TVS_INFOTIP to the window styles in Create (see list of tree-view styles). You'll also have to handle the TVN_GETINFOTIP notification message to provide the tooltip text depending on the item.

Use TVS_INFOTIP style to tree-view, and handle the TVN_GETINFOTIP notification using an ON_NOTIFY handler. Typecast the NMHDR ptr to NMTVGETINFOTOOLTIP ptr as
(NMTVGETINFOTOOLTIP *)pnmhdr and then set the tooltip string in this structure.

Pankaj's answer works if you are deriving your own control from CTreeControl.
Cassablanca's answer is correct only the code is missing.
So here are some tips from my own experience.
If you are not creating the control explicitly the GETINFOTIP style can be specified in the resource file where the control is being defined.
otherwise the style can be modified at runtime by getting the tree's window handle
HWND htreectrl = m_TreeCtrl.GetSafeHwnd();
LONG nOldStyle = GetWindowLong( htreectrl, GWL_STYLE);
LONG nNewStyle = nOldStyle & TVS_INFOTIP;
SetWindowLong( htreectrl, GWL_STYLE, nNewStyle);
To be able to handle GETINFOTIP:
If you are using the TreeControl as a member control inside a dialog:
ON_NOTIFY (TVN_GETINFOTIP, IDC_TREE, OnMyTreeGetInfoTip)
Else if you are deriving your own control from CTreeControl then use this:
ON_NOTIFY_REFLECT(TVN_GETINFOTIP, OnMyTreeGetInfoTip)
Hope this helps someone.

Related

Setting a CMFCPropertySheet as RTL

I have seen several similar question on this subject but I can seem to resolve it.
For example, on CodeProject:
https://www.codeproject.com/Messages/2873837/Re-How-to-set-RTL-layout-for-a-CPropertySheet.aspx
And on SO:
RTL layout issue for Property Sheets (MFC)
So, I have a CMFCPropertySheet that is my main application window and it is set to Arabic when the program starts:
The problem, as it the case for other users, is that whilst the pages are correctly set to RTL layout the sheet is not.
What is the correct way to get the sheet itself to display RTL?
I tried to use PreCreateWindow and it made no difference. I tried to use SetProcessDefaultLayout too. No joy.
Ideally, the window style should be changed in OnNcCreate before the window starts creating and positioning its child controls. This way, the child tab, as well as child buttons, will be positioned accordingly (OK/Cancel/Apply button will be aligned to the left side as well).
Example:
BEGIN_MESSAGE_MAP(...)
ON_WM_NCCREATE()
...
END_MESSAGE_MAP()
BOOL CMyPropertySheet::OnNcCreate(LPCREATESTRUCT pc)
{
BOOL res = CMFCPropertySheet::OnNcCreate(pc);
SetWindowLongPtr(m_hWnd, GWL_EXSTYLE,
WS_EX_LAYOUTRTL | GetWindowLongPtr(m_hWnd, GWL_EXSTYLE));
return res;
}
Alternatively, do this in OnInitDialog, use ::FindWindowEx(m_hWnd, 0, WC_TABCONTROL, 0) to find tab control's handle and change its style. This way the buttons are not re-positioned. Example:
BOOL CMyPropertySheet::OnInitDialog()
{
BOOL res = CMFCPropertySheet::OnInitDialog();
SetWindowLongPtr(m_hWnd, GWL_EXSTYLE,
WS_EX_LAYOUTRTL | GetWindowLongPtr(m_hWnd, GWL_EXSTYLE));
HWND htabctrl = ::FindWindowEx(m_hWnd, 0, WC_TABCONTROL, 0);
SetWindowLongPtr(htabctrl, GWL_EXSTYLE,
WS_EX_LAYOUTRTL | GetWindowLongPtr(htabctrl, GWL_EXSTYLE));
return res;
}
Side note:
You can also call SetProcessDefaultLayout(LAYOUT_RTL) at the start of the process (for example in CMyWinApp::InitInstance). Then change the layout depending on the result from GetProcessDefaultLayout. So you remember not to accidentally change the style for the Latin version...

Get controller who has focus inside CWnd

I've a dialog, CFormView, which holds some buttons and a panel which holds Tabcontrol, radiobuttons, text input fields etc.
So, on my panel, the CWnd, I create my input fields like this:
pEdit = new CEdit();
pEdit->CreateEx(WS_EX_CLIENTEDGE, _T("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | nAttrMultiline | m_clRect, pclPanel, iID)
Where m_clRect is a CRect, pclPanel is my CWnd, and iID is just the controller ID.
I want to fill my CEdit with text when a button is clicked, but somehow I can't get the controller who has focus.
My first attempt was to call GetFocus(), cast it into a CEdit and add the text, but this just changes the text on my button, of course.
Second attempt was to check for WM_SETFOCUS with ON_WM_SETFOCUS() and keep the previous wnd and cast it and add text, but that just changes the text on my dialog.
Third attempt was to move this to my CWnd but as far as I can see, WM_SETFOCUS is never called.
Edit:
Tried ON_WM_ACTIVATE with ::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) inside my CWnd.
But that's not being called either.
Anybody has an idea what to try next?
You just answered yourself. The correct way to do it is: on the function that handles the
ON_COMMAND(...)
of each button call
pEdit->SetWindowText(_T("text"));.
GetFocus() is wrong, because it will return the button, as when you clicked it, you just finished to put the focus on it. You can get the edit using
CEdit* pEdit= ( CEdit*) GetDlgItem(ID_OF_EDIT);
where ID_OF_EDIT is the value you passed to CreateEx as iId parameter.

Dynamic Tooltips: TTN_GETDISPINFO not send when using LPSTR_TEXTCALLBACK

I am trying to add dynamic tooltips to my mfc application. I am able to display tooltips using CToolTipCtrl with static text. But when I change the text to LPSTR_TEXTCALLBACK I don't get a TTN_GETDISPINFO notification to set the text and nothing gets displayed anymore.
I created a new MFC project from scratch and there it works. So something must be different in my application. But I am not able to find out, what I do wrong or where I should start looking.
What could be possible reasons for this behaviour? Is it possible that the notifications diverted to somewhere else?
I added the handler like this:
ON_NOTIFY(TTN_GETDISPINFO, NULL, OnToolTipNotify)
void CChildView::OnToolTipNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
wsprintf(pTTTW->szText,L"Hello World!");
}
The tooltip was added in the OnCreate function using the CMyToolTipCtrl class from Jeff Prosise MFC book:
CToolTipCtrl* tooltip = new CToolTipCtrl;
tooltip->Create(this, TTS_ALWAYSTIP);
TOOLINFO ti;
ti.cbSize = sizeof (TOOLINFO);
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
ti.hwnd = this->GetSafeHwnd ();
ti.uId = (UINT) tooltip->GetSafeHwnd ();
ti.hinst = AfxGetInstanceHandle ();
ti.lpszText = (LPTSTR) LPSTR_TEXTCALLBACK;
SendMessage (TTM_ADDTOOL, 0, (LPARAM) &ti);
If I change the second to last line to
ti.lpszText = (LPTSTR) L"Hello World!";
the tooltip gets displayed in my application, too.
It might be a problem of your project setting.
What kind of project is it? Unicode or MBCS.
If it is an MBCS message you might miss the notification because the tooltip is an unicode window and sends TTN_GETDISPINFOW!
So best practice for me is to implement always both notifications TTN_GETDISPINFOW and TTN_GETDISPINFOA.
You might check this with Spy++

C++ MFC - Not receiving CBN_EDITCHANGE message on CBS_DROPDOWN combobox in a toolbar

I have an mfc application where my mainframe contains multiple CDockablePanes.
In some of the CDockablePanes I have a CMFCToolbar and a CTreeView.
When I receive the AFX_WM_RESETTOOLBAR message in my mainframe class, I use CMFCToolBar::ReplaceButton to add a CMFCToolBarComboBoxButton to my toolbar.
This works fine and I am able to use the combobox as expected however I want to receive a notification each time the combo box's edit control's text is changed (Each key press etc) but I have not been able to catch a CBN_EDTITCHANGE message at all.
I have been using this line in my message map:
ON_CBN_EDITCHANGE(ID_TREEVIEWFILTER_COMBOBOX, Test)
But not hitting the function 'Test'. I have managed to catch other combo box messages eg. ON_CBN_DROPDOWN from in the toolbar class but not CBN_EDITCHANGE.
In my CMFCToolBarComboBoxButton derived class I override the CMFCToolBarComboBoxButton::CreateCombo function as shown:
CComboBox* FilterComboButton::CreateCombo(CWnd* pWndParent, const CRect& rect)
{
CComboBox* pCombo = new CComboBox();
if (!pCombo->Create(CBS_DROPDOWN | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_CHILD, CRect(25,0,175,FILTERTOOLBARHEIGHT), pWndParent, ID_TREEVIEWFILTER_COMBOBOX))
{
LOG("Failed to create FilterComboBox");
delete pCombo;
pCombo = NULL;
}
return pCombo;
}
Adding the code: BOOL bSuccess = pCombo->ModifyStyle(CBS_DROPDOWNLIST, NULL); after the call to CComboBox::Create causes me to receive the CBN_EDITCHANGE, but interferes with the function of the combobox.
I have managed to catch the edit controls message EN_UPDATE but not EN_CHANGE. This is so close but so far away...
Any help would be much appreciated, thanks in advance!

Tool tips for custom made control using CToolTipCtrl ? (MFC)

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.
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);
Optionally, you can also set the delay time:
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.Pop();
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.
For those that may still be looking for an answer to this as I was. (I couldn't get the one above to work - Things in MFC may have changed.)
All code is contained within the custom control class.
In your class definition add:
CToolTipCtrl ToolTip;
In PreSubclassWindow() add:
#define TOOLTIP_ID 1
ToolTip.Create(this, TTS_ALWAYSTIP );
CRect rc;
GetClientRect(rc);
ToolTip.AddTool(this, "Tool tip text", rc, TOOLTIP_ID);
In PreTranslateMessage() add:
ToolTip.RelayEvent(msg);
Whenever your tool tip text changes add:
ToolTip.UpdateTipText("New Tip Text", this, TOOLTIP_ID);