Adding item to CTreectrl C++ - 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.

Related

C++ MFC CListCtrl - How to sort item upon startup (without any UI interaction)?

I have a list of item to be loaded in a MFC window with CListCtrl in it.
Upon OnInitDialog(), I will perform
m_List.InsertItem(&item);
where this item contain data such as: filename, last date modified, comment.
The current output that I have now is that it will sort it with last date modified when I open this dialog, then I would need to manually click on the list header to trigger an event to sort the data according to filename with functions such as below:
ON_NOTIFY(LVN_COLUMNCLICK, IDC_FILELIST, OnColumnclickFilelist)
int CALLBACK CDlg::AlphaNumericSorting(LPARAM lParam1, LPARAM lParam2, LPARAM ParameterSort)
{
std::wstring wv1(((CLVData*)lParam1)->strFileName);
std::wstring wv2(((CLVData*)lParam2)->strFileName);
return StrCmpLogicalW(wv1.c_str(), wv2.c_str()) < 0;
}
void Dlg::OnColumnclickFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
m_List.SortItems(AlphaNumericSorting, pNMListView->iSubItem);
*pResult = 0;
}
Now I'm trying to make it sort automatically upon OnInitDialog(), is there a correct way to do so? From what I searched so far, all samples I got are those with event handling.

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.

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.

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.

How to find the menu item (if any) which opens a given HMENU when activated?

I'd like to implement a function with the prototype
/* Locates the menu item of the application which caused the given menu 'mnu' to
* show up.
* #return true if the given menu 'mnu' was opened by another menu item, false
* if not.
*/
bool getParentMenuItem( HMENU mnu, HMENU *parentMenu, int *parentMenuIdx );
Given a HMENU handle, I'd like to be able to find out which menu item (if any) in the application opened it. This is basically the reverse of the GetSubMenu function.
My current approach is to look into each HMENU of the top level windows of the application and check for whether I can find a menu item which would open the given sub menu when activated. I do this recursively, using GetMenuItemCount/GetSubMenu.
This is rather inefficient though, and it fails for menus which are opened by context menu items. Hence, I'm wondering:
Does anybody have a nice idea how to find the menu item (if any) which opens a given HMENU when activated?
UPDATE: An idea which just came to my mind; it should be possible (using the SetWindowsHookEx function) to install a hook which gets notified of all input events which happened in a menu. Whenever a menu item activation is detected, memorize the menu item (identified by a (HMENU,int) pair) and the HMENU which will get opened by the menu item in a global map. The getParentMenuItem function above could then simply perform a lookup into the map.
UPDATE to the update: The hooking idea described in the update above won't work as it is since it will of course only recognize menu item -> menu associations for items which have been activated at some point.
This feels a bit ugly though since it reqiures me to keep a lot of state (the map); are there any easier possibilities?
You could try setting MENUINFO.dwMenuData to the parent menu handle for all menus you create in your application:
MENUINFO mi;
mi.cbSize = sizeof(MENUINFO);
mi.dwMenuData = (ULONG_PTR)<parent HMENU if this is a sub menu>
mi.fMask = MIM_MENUDATA;
SetMenuInfo(hCreatedMenu, &mi);
Then you only need to query this dwMenuData field in your function:
bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx)
{
MENUINFO mi;
mi.cbSize = sizeof(MENUINFO);
mi.fMask = MIM_MENUDATA;
if (!GetMenuInfo(mnu,&mi) || mi.dwMenuData == 0)
return false;
*parentMenu = (HMENU)mi.dwMenuData;
// not sure how or why you need the parentMenuIdx, but you should be able
// to derive that from the parent HMENU
return true;
}
Edit: If you don't have control over how all menus are created, you could use a WH_CALLWNDPROC hook to trap when a menu is first created. A good article (with source code) describes how this can be done - you could then look at trying to inject the parent HMENU into the created menu using the method described above.
I just found the same need. I have my menus defined in the .rc file, and I want to gray out access to a popup menu if all of its subitems get grayed out. (You can argue that this inhibits discovery, but, in this particular case, it's what we need).
As previous responders mentioned, if you are creating menus programmatically, you can store an item's parent menu handle as ancillary information.
But for menus defined in the resource file using the POPUP keyword, you can't associate an ID with a POPUP and you can't easily climb up the menu tree programmatically. You have to recursively search down for a menu item, and keep track of the parents.
I wrote the following code to do this. EnableSubmenuItem works like EnableMenuItem to enable or disable a menu item by ID. It then checks the item's parent menu. If all items in its parent menu are disabled, the parent gets disabled. Conversely if any subitem is enabled, the parent gets enabled.
(BTW the original question, how to find parent hMenu of a menu item, is addressed by routine FindParentMenu, in the following code).
////////////////////////////////////////////////////////////////////////////////
// MenuContainsID - return TRUE if menu hMenu contains an item with specified ID
////////////////////////////////////////////////////////////////////////////////
static BOOL MenuContainsID (HMENU hMenu, UINT id)
{
int pos; // use signed int so we can count down and detect passing 0
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf)); // request just item ID
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_ID;
for (pos = GetMenuItemCount(hMenu); --pos >= 0; ) // enumerate menu items
if (GetMenuItemInfo(hMenu, (UINT) pos, TRUE, &mf)) // if we find the ID we are looking for return TRUE
if (mf.wID == id)
return TRUE;
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////
// MenuItemIsSubmenu - returns TRUE if item # pos (position) of menu hMenu is a
// submenu. Sets phSubMenu to menu handle if so
////////////////////////////////////////////////////////////////////////////////
static BOOL MenuItemIsSubmenu (HMENU hMenu, UINT pos, HMENU *phSubMenu)
{
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf)); // request just submenu handle
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_SUBMENU;
if (! GetMenuItemInfo(hMenu, pos, TRUE, &mf)) // failed to get item?
return FALSE;
*phSubMenu = mf.hSubMenu; // pass back by side effect
return (mf.hSubMenu != NULL); // it's a submenu if handle is not NULL
}
////////////////////////////////////////////////////////////////////////////////
// MenuItemIsEnabled - returns true if item # pos (position) of menu hMenu is
// enabled (that is, is not disabled or grayed)
////////////////////////////////////////////////////////////////////////////////
static BOOL MenuItemIsEnabled (HMENU hMenu, UINT pos)
{
return ! (GetMenuState(hMenu, pos, MF_BYPOSITION) & (MF_GRAYED | MF_DISABLED));
}
////////////////////////////////////////////////////////////////////////////////
// FindParentMenu - returns handle of the submenu of menu bar hMenu that contains
// an item with id "id". Position of this submenu is passed by side effect to
// pParentPos, and /its/ parent menu is passed by side effect through phGrandparentMenu.
////////////////////////////////////////////////////////////////////////////////
static HMENU FindParentMenu (HMENU hMenu, UINT id, HMENU *phGrandparentMenu, UINT *pparentPos)
{
int pos; // use signed int so we can count down and detect passing 0
HMENU hSubMenu, hx;
for (pos = GetMenuItemCount(hMenu); --pos >= 0; ) {
if (MenuItemIsSubmenu(hMenu, (UINT) pos, &hSubMenu)) {
if (MenuContainsID(hSubMenu, id)) {
*phGrandparentMenu = hMenu; // set grandparent info by side effect
*pparentPos = (UINT) pos;
return hSubMenu; // we found the item directly
}
if ((hx = FindParentMenu(hSubMenu, id, phGrandparentMenu, pparentPos)) != NULL)
return hx; // we found the item recursively (in a sub-sub menu). It set grandparent info
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
// AllSubitemsAreDisabled - returns TRUE if all items in a submenu are disabled
////////////////////////////////////////////////////////////////////////////////
static BOOL AllSubitemsAreDisabled (HMENU hMenu)
{
int pos; // use signed int so we can count down and detect passing 0
for (pos = GetMenuItemCount(hMenu); --pos >= 0; )
if (MenuItemIsEnabled(hMenu, (UINT) pos))
return FALSE; // finding one enabled item is enough to stop
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
// EnableSubMenuItem - like EnableMenuItem, enables or disables a menu item
// by ID (only; not position!) where hMenu is top level menu.
// Added bonus: If the item is in a pop-up menu, and all items in the popup are
// now disabled, we disable the popup menu itself.
//
// Example:
// EnableSubMenuItem(hMainMenu, IDM_CONFIGURE, MF_GRAYED);
//
////////////////////////////////////////////////////////////////////////////////
void EnableSubMenuItem (HMENU hMenu, UINT id, UINT enable)
{
HMENU hParentMenu, hGrandparentMenu;
UINT parentPos;
// EnableMenuItem does its job recursively and takes care of the item
EnableMenuItem(hMenu, id, enable | MF_BYPOSITION);
// But popup menus don't have IDs so we have find the parent popup menu, and its parent (the
// grandparent menu), so we can enable or disable the popup entry by position
if ((hParentMenu = FindParentMenu(hMenu, id, &hGrandparentMenu, &parentPos)) != NULL)
EnableMenuItem(hGrandparentMenu, parentPos,
MF_BYPOSITION | (AllSubitemsAreDisabled(hParentMenu) ? MF_GRAYED : MF_ENABLED));
}