get lparam value form the htreeitem c++ - 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.

Related

How to pass a tree selection to another tree selection view in MFC

I have two tree view in MFC window application. Tree1 and Tree2
If i select any one item in tree1 and the same one should find and get select in tree2.
My code is like below.
Selecting first tree item and trying to pass the item to Tree2
void TreePage::TreeSelHandle(NMHDR* pNMHDR, LRESULT* pResult)
{
CTreeCtrl m_ctlTree1, m_ctlTree2;
m_ctlTree1.OnTvnSelTreeItem(pNMHDR, pResult);
NMHDR nmhdr;
nmhdr.code = TVN_SELCHANGED;
nmhdr.hwndFrom = m_ctlTree2.GetSafeHwnd();
nmhdr.idFrom = IDC_MODELVIEW_TREE2;
if (m_ctlTree2.GetSafeHwnd())
m_ctlTree2.OnTvnSelTreeItem(&nmhdr, pResult);
}
TreeSelection code is below.
void UITreePageCtrl::OnTvnSelTreeItem(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
CString strTreeText;
Select(pNMTreeView->itemNew.hItem, TVGN_CARET | TVGN_FIRSTVISIBLE |
TVGN_DROPHILITE);
SelectItem(pNMTreeView->itemNew.hItem);
strTreeText = GetItemText(pNMTreeView->itemNew.hItem);
*pResult = 0;
}
This is not working. Tree2 selection get failure. GetItemText return null char.
How to fix this.? Thanks in Advance.

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.

Adding item to CTreectrl C++

I have a requirement of converting a project from VB to C++.
There is Tree control in VB.
For adding items to Tree control "Add" Method(Nodes Collection)
has been used, which contains a parameter called "key" and the same key can be retrieved on click of that particular item.
Is there any such provision in CTreeCtrl InsertItem function using TVITEM or TVITEMEX structure where we can add a key to each item of the tree control and get it back when clicked on it?
To create your root item:
TV_INSERTSTRUCT tvInsertStruct;
tvInsertStruct.hParent=NULL;
tvInsertStruct.hInsertAfter=TVI_LAST;
tvInsertStruct.item.pszText=_T("ROOT");
tvInsertStruct.item.mask=TVIF_TEXT;
const HTREEITEM hRootItem= m_tree.InsertItem(&tvInsertStruct);
To insert sub-items hanging on the root:
for(int i=0; i<SomeCollection.GetCount(); i++)
{
const CElement* pElement= SomeCollection.GetAt(i);
ASSERT(pElement);
CString Name = pElement->GetName();
tvInsertStruct.hParent = hRootItem;
tvInsertStruct.hInsertAfter = TVI_LAST;
const LPTSTR cutJobNameTemp = Name.GetBuffer(0);
tvInsertStruct.item.pszText = cutJobNameTemp;
tvInsertStruct.item.mask = TVIF_TEXT;
HTREEITEM hItem = m_tree.InsertItem(&tvInsertStruct);
ASSERT(hItem);
tree.SetItemData(hItem, (DWORD_PTR)pElement);
}
The code line that answers your question is SetItemData: with it you can directly associate a tree node handle with a memory address.111
To see all nodes open just add:
ExpandTreeCtrl(m_tree);
NOTE: I know the following is not the cleanest approach to handle the selection of an item on the tree, so I replaced it with a more proper way that also handles keyboard
To get an entry point for your app to respond to Clicks on the tree, you can add in its parent dialog (or control)'s message map
ON_NOTIFY(NM_CLICK, IDC_TREE, OnNMClickTree)
and implement its handling function
void CMyDialog::OnNMClickTree(NMHDR *pNMHDR, LRESULT *pResult)
{
UINT flags;
CPoint point;
GetCursorPos(&point);
*pResult= 0;
CTreeCtrl* pTree= dynamic_cast <CTreeCtrl*> (this->GetDlgItem(pNMHDR->idFrom));
if(pTree)
{
pTree->ScreenToClient(&point);
HTREEITEM hItem = pTree->HitTest(point, &flags);
if( (flags & TVHT_ONITEMINDENT) || (flags & TVHT_ONITEMBUTTON) ) //do nothing when clicking on the [+]expand / [-]collapse of the tree
return;
if(!hItem)
return;
// If you want to get item text:
CString sText= pTree->GetItemText(hItem);
//To get your element:
CElement* pElement = (CElement*)pTree->GetItemData(hItem);
}
}
To get an entry point for your app to respond to the change of the currently selected item on the tree, you can add in its parent dialog (or control)'s message map
ON_NOTIFY(TVN_SELCHANGED,IDC_TREE, OnTreeCtrlSelChanged)
and implement its handling function
void CMyDialog::OnTreeCtrlSelChanged(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*) pNMHDR;
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
if(!hItem)
return;
// If you want to get item text:
CString sText= m_tree.GetItemText(hItem);
//To get your element:
CElement* pElement = (CElement*)m_tree.GetItemData(hItem);
}
The line that is now dereferencing to access the CElement data associated with the tree node is GetItemData. Then do what you intend with the pointer you got.

NM_TREEVIEW* of tree control

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)

Re-expand treeview item

I am working on an old MFC application. There is a TreeView control in the application. OnItemExpanding function is overridden. I am getting children of a TreeViewItem by it is expanded. If a node is expanded for the first time its children are populated. If there are no children to an item then the expand icon (+ sign) removes from the TreeViewItem.
Now problem is that I expanded one node which doesn't have children. After doing some work children are added to that node. But now I cannot get newly added children as expand icon is missing. How I can refresh that particular node in the TreeView. I have created a refresh button. In that I am able to find my current selected node in the TreeView, but what next.
Here is the code.
void CMyTreeView::OnItemExpanding(CTreeCtrl& tree, NMHDR* pNMHDR, LRESULT* pResult)
{
//This is only called when I click on expand (+ sign)
//some check here which populates children.
}
void CMyTreeView::RefreshNode(CTreeCtrl& tree, HTREEITEM selectedNode)
{
// What should I do here?
}
You have to set cChildren of TVITEM to 'one':
TVITEM tvItem = {0};
tvItem.mask = TVIF_HANDLE | TVIF_CHILDREN;
tvItem.hItem = selectedNode;
tvItem.cChildren = 1;
tree.SetItem(&tvItem);
You are trying to reinvent what common controls library already can do for you.
What you need to do is, when you insert a "folder" item set itemex.cChildren = I_CHILDRENCALLBACK which will tell the tree to send you TVN_GETDISPINFO notification when it needs to know if the item has children. It will then similarly send TVN_GETDISPINFO for every individual child.
It will only send the notifications when it is absolutely necessary, so you won't need to do any expensive stuff in vain.
I would say, you need to change the ItemState: http://msdn.microsoft.com/de-de/library/ce034e69%28v=vs.80%29.aspx
BOOL SetItemState(
HTREEITEM hItem,
UINT nState,
UINT nStateMask
);
Take a look at the HTREEITEM:
typedef struct tagTVITEM {
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
} TVITEM, *LPTVITEM;
cChildren Type: int
Flag that indicates whether the item has associated child items. This member can be one of the following values.