NM_TREEVIEW* of tree control - c++

I am dealing with NM_TREEVIEW, where from above structure i am only concentrate on itemNew.lParam.
For example,
void CCtrlDlgTree::OnSelChangedTreeCtrl(
NMHDR* pNMHDR,
LRESULT* pResult
)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
pNMTreeView->itemNew.lParam;
}
In above example i am getting correct pointer of NM_TREEVIEW. But when i apply same logic for below function
void CCtrlModDefDlgTree::OnNMRClick(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
pNMTreeView->itemNew.lParam;
}
I getting garbage value of NM_TREEVIEW pointer.
Please help me how to access NM_TREEVIEW in OnNMRClick or how access pNMTreeView->itemNew in OnNMRClick?

If what you are trying to achieve is displaying context menu, here is the KB article for you:
How To Display a Context Menu for CTreeCtrl (KB222905)
As for the code, this cast:
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
looks incorrect, as "Right Click" notification message does not send NM_TREEVIEW structure as lParam, but just NMHDR structure.

As your said you want lParam of Tree node in following function,
void CCtrlModDefDlgTree::OnNMRClick(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
pNMTreeView->itemNew.lParam;
}
It is impossible as per me to retrive lParam from *pNMHDR. But if you successfully getting HTREEITEM in this function then you can easily get lParam like in following Example,
CCtrlModDefDlgTree::OnNMRClick(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
/* Get the cursor position for this message */
DWORD dwPos = GetMessagePos();
/* Convert the co-ords into a CPoint structure */
CPoint pt( GET_X_LPARAM( dwPos ), GET_Y_LPARAM ( dwPos ) );
CPoint spt;
spt = pt;
/* convert to screen co-ords for the hittesting to work */
ScreenToClient( &spt );
UINT test;
HTREEITEM hti = HitTest( spt, &test );
TVITEM tvi;
tvi.hItem = hti;
tvi.mask = TVIF_PARAM;
TreeView_GetItem(GetSafeHwnd(),&tvi);
}
And from TVITEM* you easily get lParam of tree node.

Just read the docks. NM_RCLICK does not give you a pointer to a treeview item!
It is just a pointer to NMHDR!
Just read the MSDN docs.
It may be easier if you use WM_RBUTTONDOWN and determine the item hit by yourself (use TVM_HITTEST)

Related

Detecting Up / Down arrows in CSpinButtonCtrl MFC C++

Is there any way to differentiate when the Up or Down arrow of a CSpinButtonCtrl is pressed?
I am trying to use the OnPointerdown event, but I don't know how to do it...
afx_msg LRESULT CMySpinButtonCtrl::OnPointerdown(WPARAM wParam, LPARAM lParam)
{
if(IS_POINTER_PRIMARY_WPARAM(wParam))
{
//TODO
}
return 0;
}
I will appreciate any kind of help.
Is there any way to differentiate when the Up or Down arrow of a CSpinButtonCtrl is pressed?
You should use UDN_DELTAPOS to do this.
Right-click the control in the Resource Editor and select Add Event Handler:
Select the UDN_DELTAPOS message and click Add and Edit:
You will be provided with skeleton code:
void CMFCApplication1Dlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
The NMUPDOWN article explains about the structure that you use. What you need to do is test the iDelta value. Example:
void CColumnOrderDlg::OnDeltaposSpinColumns(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
if (pNMUpDown != nullptr)
{
if( pNMUpDown->iDelta > 0)
// Up - Do stuff;
else if(pNMUpDown->iDelta < 0)
// Down - Do stuff;
}
*pResult = 0;
}
There is also a useful article here where it states:
If you use a spin control for some other purpose, for example, to page through a sequence of windows or dialog boxes, then add a handler for the UDN_DELTAPOS message and perform your custom action there.

Display formatted text on selecting item in the Combobox

I have a combobox in that I want to display different string on selecting an item in Combo.
My combo box is a dropdown combobox.
For eg: I have following in my combobox.
Alex - Manager
Rain - Project Lead
Shiney - Engineer
Meera - Senior Engineer
OnSelecting an item in combobox I want to diaply only name i.e. Alex.
I tried below code
struct details{
CString name;
CString des;
};
BOOL CComboTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
details d1;
d1.name = _T("alex");
d1.des =_T("manager");
m_vec.push_back(d1);
details d2;
d2.name = _T("Rain");
d2.des =_T("Engineer");
m_vec.push_back(d2);
// TODO: Add extra initialization here
for(int i=0;i<m_vec.size();i++)
{
m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
}
m_ctrlCombo.SelectString(-1,m_vec[0].name);
m_ctrlCombo.SetWindowText(m_vec[0].name);
return TRUE; // return TRUE unless you set the focus to a control
}
void CComboTestDlg::OnCbnSelchangeCombo1()
{
int nItem = m_ctrlCombo.GetCurSel();
details* det = (details*)m_ctrlCombo.GetItemData(nItem);
PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg)
{
MSG msg1=*pMsg;//I am loosing the value after checking ..so storing temp.
MSG msg;
CopyMemory(&msg, pMsg, sizeof(MSG));
HWND hWndParent = ::GetParent(msg.hwnd);
while (hWndParent && hWndParent != this->m_hWnd)
{
msg.hwnd = hWndParent;
hWndParent = ::GetParent(hWndParent);
}
if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
{
OnCbnSelchangeCombo1();
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.
I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).
Install the sub class proc in OnInitDialog():
COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}
ComboEditSubClassProc() could look like this:
LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case WM_SETTEXT:
{
CString text = reinterpret_cast<LPCTSTR>( lParam );
// Extract the name (everything before "-").
CString name = text.SpanExcluding( _T("-") );
name.TrimRight();
// Forward the modified text to any other sub class procs, aswell
// as the original window proc at the end of the chain.
return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
}
case WM_NCDESTROY:
{
// We must remove our subclass before the subclassed window gets destroyed.
// This message is our last chance to do that.
RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
break;
}
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
Notes:
Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.
I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.
There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.

get lparam value form the htreeitem c++

I created a dialog with a tree control which fetches data on to a list control when clicked on any particular node of the treecontrol. This is how i tried inserting nodes.
CString *sCommonAppkey = new CString(_szApp + sIsPath);
HTREEITEM hrCommon = m_cTreeCtrl.InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM, _T("Common"), icoPlanit, icoPlanit, 0, 0, (LPARAM)(LPCTSTR)sCommonAppkey, NULL, NULL);
When clicked on a node it is being directed to the event handler "OnTvnSelchangedExample"
and the data is fetched from the path specified in "lparam" parameter in the insertitem method of HTREEITEM.
void **CExample**::OnTvnSelchangedExample(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
LPARAM lp = pNMTreeView->itemNew.lParam;
CString *sTempKey = (CString *)lp;
CString path = sTempKey->GetBuffer();
}
I can access the lparam value only in the event handler.
Now i want to implement search functionality for the entire tree's data.
so i need the lparam value of all Tree handles sequentially by iterating through it, so that i can search for the specific text in the tree.
So without clicking on any node of the tree, is there any possibility to get the lparam value of Tree handle(HTREEITEM)
You can iterate through the tree from the root with TreeView_GetChild, there the handle is the tree handle. To get the handle, call TreeView_GetItem.
TVITEMEX item;
item.mask = TVIF_PARAM;
item.hItem = hrCommon;
TreeView_GetItem(handle_, &item);
CString* text = (CString*)item.lParam;
The tree traversal is easy to implement using recursion:
void CMyTreeCtrl::Iterate(HTREEITEM hItem)
{
if (hItem)
{
// Use the tree node corresponding to hItem
// .....
// End of using hItem
hItem = GetNextItem(hItem, TVGN_CHILD);
while (hItem)
{
Iterate(hItem);
hItem = GetNextItem(hItem, TVGN_NEXT);
}
}
else
{
HTREEITEM hItem = GetNextItem(NULL, TVGN_ROOT);
while (hItem)
{
Iterate(hItem);
hItem = GetNextItem(hItem, TVGN_NEXT);
}
}
}
If you want to get the item data you need to simply call GetItemData(hItem). It returns DWORD_PTR. So in your case you need to cast it to CString*. That's it.
IMPORTANT: In this example CMyTreeCtrl is derived from CTreeCtrl.

Properly handle WM_PASTE in subclass procedure

RELEVANT INFORMATION:
I have a subclass procedure that needs to validate the content of the clipboard before it gets pasted.
I have managed to get the content of the clipboard successfully, at least I think so.
QUESTION:
I do not know how to construct the following if statement ( the following is pseudo code ):
if( clipboard content is OK )
defaul handler;
else
discard message;
MY EFFORTS TO SOLVE THIS:
So far this is what I have in mind:
LRESULT CALLBACK Decimalni( HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
case WM_PASTE:
{
bool IsTextValid = true; // indicates validity of text
if( OpenClipboard(hwnd) ) // open clipboard
{
HANDLE hClipboardData;
// get clipboard data
if( hClipboardData = GetClipboardData(CF_UNICODETEXT) )
{
// Call GlobalLock so that to retrieve a pointer
// to the data associated with the handle returned
// from GetClipboardData.
wchar_t *pchData = (wchar_t*)GlobalLock(hClipboardData);
// copy clipboard data so we can free clipboard
wchar_t result[10]; // I just need first 9 characters
memset( result, L'0', sizeof(result) );
// copy clipboard data WITH TRUNCATION!!!
wcsncpy_s( result, 10, pchData, _TRUNCATE );
// Unlock the global memory.
GlobalUnlock(hClipboardData);
/*** parse the text for validity ****/
// code for parsing text
// update IsTextValid to indicate success or fail
/*** end of parsing *******/
}
// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
CloseClipboard();
}
// here should be the problematic if statement
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
return FALSE;
}
break;
case WM_CHAR:
{
// filter out some invalid keys
}
break;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, Decimalni, 0 ); // remove subclassing
break;
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
Is my idea correct or is there another way to form my if statement?
Thank you.
Best regards.
The code seems plausible, down to the action taken. Bit clunky, but that's Windows API. There may be better ways, but this should work.
One mistake: if the text is OK, you should call DefSubclassProc, not the default window procedure.
If the text is not OK you could consider emptying the clipboard. There is not enough here about your other requirements to talk about that.

MFC how to know a resizing of view is finished

I am wondering how to catch the fact that a view (CView in a CMDIChildWnd frame) has been resized, and that the user just released the left mouse button.
None of OnSize, OnSizing and OnLButtonUp work.
I have tried the solution in http://www.codeguru.com/forum/showthread.php?t=59476 and it doesn't work.
I am working on VC2010 with W7.
Thanks in advance.
Try WM_NCLBUTTONUP. I don't know if a view can be resized other than by the mouse, but if it can you probably also want to respond to WM_EXITSIZEMOVE as in the link you gave.
I recently needed to do this. I used a combination of OnSysMessage to capture the SC_SIZE and SC_MOVE event and WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE within the CMDIChildWnd derived class. Within the OnSysMessage handler I set a public variable for IsSizing. I can then check the variable within the OnEnterSizeMove function and act accordingly. Within the OnExitSizeMove function, it resets the variable to FALSE.
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
And for the handlers:
void CChildFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
switch(nID&0xfff0)
{
case SC_SIZE:
m_bIsSizing = TRUE;
break;
case SC_MOVE:
m_bIsSizing = FALSE;
break;
}
CMDIChildWnd::OnSysCommand(nID, lParam);
}
LRESULT CChildFrame::OnEnterSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnEnterSizeMove\n");
}
return 0; //don't flag we processed the message
}
LRESULT CChildFrame::OnExitSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnExitSizeMove\n");
m_bIsSizing = FALSE; // set to false before calling OnSizing
CRect dlgRect;
pView->GetClientRect(dlgRect);
pView->InvalidateRect(NULL, FALSE);
pView->SendMessage(WM_SIZE, WPARAM(SIZE_RESTORED), MAKELONG(dlgRect.Width(), dlgRect.Height()));
}
return 0; //don't flag we processed the message
}
Then within your View code, you can check if the Frame is sizing within your OnSize handler.
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CChildFrame* pFrame=(CChildFrame*)GetParentFrame();
if(!pFrame->m_bIsSizing) {
CWaitCursor cur;
//DO UPDATE CODE
}
}