CTreeCtrl - getting an item position - c++

Is there a way of getting the position (index) of an item in a CTreeCtrl?
I am interested in the index of a node at its particular level.
I was thinking to maintain the item positions within the item "data" field, but the problem is that my tree is sorted and I cannot predict the position an item will receive (well, only if I sort the items in advance which I would like to avoid).

I don't think you can. I assumed that maybe the control could be treated as an array (maybe it still can but I can't find a reference).
Anyways, there are no member functions (according to the MFC API) that give you access to that information

/// there is another way if you "Use Unicode Character Set" (visual studio)
/// Properties->General->Character Set
CPoint point;
GetCursorPos(&point);
m_Tree.ScreenToClient(&point);
UINT nHitFlags;
HTREEITEM hItem = m_Tree.HitTest(point, &nHitFlags);
int idx = m_Tree.MapItemToAccId(hItem);

Get the node handle and then iterate over the elem
Iterate over all the elements, while you count the elements, till you reach the right item?

int GetIndex(const CString & a_Cstr)
{
int idx = 0;
std::vector<CString>::const_iterator _begIt = m_RulesVec.begin();
std::vector<CString>::const_iterator _PosIt = find(m_RulesVec.begin(), m_RulesVec.end(), a_Cstr);
if (_PosIt == m_RulesVec.end()) {
return -1;
}
else {
while (_begIt != _PosIt) {
++idx;
++_begIt;
}
return idx;
}
}
/// it can(must) be done in this function
/// OnNMClickRulesTree(NMHDR *pNMHDR, LRESULT *pResult)
// Create vector like this
std::vector<CString> Vec{"first", "second", "third" };
// OnInit insert items to CtreeCtrl like this
m_Tree.InsertItem("first", hItem);
m_Tree.InsertItem("second", hItem);
m_Tree.InsertItem("third", hItem);
// then get cur selected item like this
CPoint point;
GetCursorPos(&point);
m_Tree.ScreenToClient(&point);
UINT nHitFlags;
HTREEITEM hItem = m_Tree.HitTest(point, &nHitFlags);
// get item text
CString Cstr = m_Tree.GetItemText(hKid);
int idx = GetIndex(Cstr);

Related

CListCtrl do action based on selected row values

I have to enable/disable buttons on a dialog based on values in a CListViewCtrl. Based on the selected row. I got this far:
NOTIFY_HANDLER(IDC_LIST, LVN_ITEMCHANGED, OnMyListChange)
// ....
LRESULT OnMyListChange(int, LPNMHDR pNMHDR, BOOL&)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if ((pNMListView->uChanged & LVIF_STATE)
&& (pNMListView->uNewState & LVIS_SELECTED))
{
// enable/disable buttons based on row field value
}
return 0;
}
Lets say I have column1 column2 column3. I need to write a condition based on column2 value in the selected row. Multiple row selection is not a case. Thank you.
There is a method GetItemText. Notice it refers to:
nItem
The index of the item whose text is to be retrieved.
nSubItem
Specifies the subitem whose text is to be retrieved.
Think of them as row and column. Now, look at the NM_LISTVIEW structure in your handler:
typedef struct tagNMLISTVIEW {
NMHDR  hdr;
int    iItem;
int    iSubItem;
UINT   uNewState;
UINT   uOldState;
UINT   uChanged;
POINT  ptAction;
LPARAM lParam;
} NMLISTVIEW, *LPNMLISTVIEW;
It too has those properties:
int    iItem;
int    iSubItem;
So you should be able to get at the item text and perform what you wanted to do. Example:
// Get text in column 2 (it might 1 - can't remember if it is zero based indexing)
CString strValue = m_myList.GetItemText(pNMListView->iItem, 2);
if(strValue == "DoThis")
{
// ...
}
The above code is not tested!!

How to iterate through all parent nodes of treeview control in c++?

I am new to C++. Is there any way to iterate through all parent nodes starting from the tree ?
Ex :
ParentNode1->Child1
->Child2
ParentNode2->Child1
->Child2
I want to iterate from ParentNode1 to ParentNode2.
Kindly suggest.
Use the following functions to traverse the MFC CTreeCtrl:
void TraverseTree(CTreeCtrl* pTreeCtrl)
{
TraverseTreeBranch(pTreeCtrl, pTreeCtrl->GetRootItem(), TVI_ROOT);
}
void TraverseTreeBranch(CTreeCtrl* pTreeCtrl, HTREEITEM hItem, HTREEITEM hParentItem)
{
// Do stuff with hItem here
HTREEITEM hItemTmp = pTreeCtrl->GetChildItem(hItem);
while(hItemTmp != NULL)
{
TraverseTreeBranch(pTreeCtrl, hItemTmp, hNewItem);
hItemTmp = GetNextSiblingItem(hItemTmp);
}
}
The CTreeView class wraps a CTreeCtrl object. The latter provides CTreeCtrl::GetNextSiblingItem/CTreeCtrl::GetPrevSiblingItem. Those members return the next/previous treeview item, that's on the same hierarchy level.
If you are looking for a Windows API solution, use the TreeView_GetNextSibling/TreeView_GetPrevSibling macros instead, or send a TVM_GETNEXTITEM message.

How to get multiline tooltip for a list control on a dialog box?

As text on my list box is very huge I am trying to get multiline a tooltip on the list control.
BOOL CTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
mylist.EnableToolTips(TRUE);
mylist.SetExtendedStyle(LVS_EX_INFOTIP | mylist.GetExtendedStyle());
mylist.InsertColumn(0, L"suri", LVCFMT_LEFT, 10000);
CString str1 = L"nonNegativeInteger GetVehicleOwnerHolderByRegNumAndDateResponse.GetVehicleOwnerHolderByRegNumAndDateResult[optional].GetVehicleOwnerHolderByRegNumAndDateResultType.VHOwnerHolderResponse.VHOwnerHolderResponseType.Body.VehicleCountries.VehicleCountriesType.VehicleCountry[1, unbound].VehicleCountryType.VehCountryReplies.VehCountryRepliesType.VehCountryReply[1, unbound].Messages[optional].Message[1, unbound].MessageType.MessageCode";
for (int i = 0; i < 20 ; i++) {
CString str2;
str2.Format(L"%d",i);
str2 = str2 + str1;
mylist.InsertItem(LVIF_TEXT | LVIF_PARAM, i, str2, 0, 0, 0, NULL);
}
return TRUE; // return TRUE unless you set the focus to a control
}
I am getting following output which is truncated text i.e complete text is missing.
How to get text on tooltip multiline?
EDIT: I used following also.
still same result.
CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
if (pToolTip)
pToolTip->SetMaxTipWidth(SHRT_MAX);
You can get multi-line tooltips using newlines-charecters with SetMaxTipWidth() set to a large value. And if calling SetMaxTipWidth() with a small value, then it will automatically break into multiple lines when meeting a space-character.
You need to subclass your tooltip/infotip in order to use it:
BEGIN_MESSAGE_MAP(CListCtrl_InfoTip, CListCtrl)
ON_NOTIFY_REFLECT_EX(LVN_GETINFOTIP, OnGetInfoTip)
ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnToolNeedText)
ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnToolNeedText)
END_MESSAGE_MAP()
void CListCtrl_InfoTip::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
SetExtendedStyle(LVS_EX_INFOTIP | GetExtendedStyle());
}
BOOL CListCtrl_InfoTip::OnGetInfoTip(NMHDR* pNMHDR, LRESULT* pResult)
{
// Will only request tooltip for the label-column
NMLVGETINFOTIP* pInfoTip = (NMLVGETINFOTIP*)pNMHDR;
CString tooltip = GetToolTipText(pInfoTip->iItem, pInfoTip->iSubItem);
if (!tooltip.IsEmpty())
{
_tcsncpy(pInfoTip->pszText, static_cast<LPCTSTR>(tooltip), pInfoTip->cchTextMax);
}
return FALSE; // Let parent-dialog get chance
}
BOOL CListCtrl_InfoTip::OnToolNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
...
// Break tooltip into multiple lines if it contains newlines (\r\n)
CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
if (pToolTip)
pToolTip->SetMaxTipWidth(SHRT_MAX);
...
}
There are two aspects to consider:
1. The size of the window
To activate multilines mode. This instruction is sufficient :
pToolTip->SetMaxTipWidth(SHRT_MAX);
2. Number of characters to display
For this second point it is necessary to be wary because the size of the field pszText is limited to 80 characters:
typedef struct tagNMTTDISPINFOA {
NMHDR hdr;
LPSTR lpszText;
char szText[80];
...
}
Therefore, even if you change SetMaxTipWidth you will not see any difference.
I suggest you to use the lpszText field which has no limit. Below is the code fragment that interests you:
pTTTW->lpszText = T2W (strTipText.GetBuffer (strTipText.GetLength ()));
Where strTipText is the CString that contains the message to show in the pop-up

How can i find treeview node by which label equals string variable?

I would like to start by saying thanks to everyone who takes some time to view this thread and try to help.
I have searched the Internet, and couldn't find an example of selecting tree view node that has label text as same as text of a string variable.
On MSDN I have found message TVM_GETISEARCHSTRING but I don't know if it can be used to solve my problem. Even if it can, I still don't know how to use it.
I have a string variable that holds text from database.
When program loads, tree view should have a node with the same text selected.
Please help with some instructions or code snippets, since I have no clue how to even start coding this.
I work in MS Visual Studio Express 2008, on Windows XP, in C++, using pure WIN32 API.
That would be all, again I thank everyone who tries to help.Thank you very very much!
EDIT:
Both answers are good for me, but I don't know how to mark them both, it seems that on this site only one answer can be accepted.
I couldn't have just neglected all the work both of them invested to help me, so I write this in order to try to repay to the Jonathan at least by officially stating that his solution is acceptable for me too, it is just that Tim's solution suits my coding style better.I will also upvote both answers.
The treeview control does not provide an API to search for a label. You will have to manually traverse the items and compare them to your string.
If your treeview is more than one level deep you will have to decide how to traverse the items (either depth first or breadth first). In case there are multiple items with the same label these strategies may return different items.
An implementation might look something like this:
// Helper function to return the label of a treeview item
std::wstring GetItemText( HWND hwndTV, HTREEITEM htItem )
{
static const size_t maxLen = 128;
WCHAR buffer[ maxLen + 1 ];
TVITEMW tvi = { 0 };
tvi.hItem = htItem; // Treeview item to query
tvi.mask = TVIF_TEXT; // Request text only
tvi.cchTextMax = maxLen;
tvi.pszText = &buffer[ 0 ];
if ( TreeView_GetItem( hwndTV, &tvi ) )
{
return std::wstring( tvi.pszText );
}
else
{
return std::wstring();
}
}
This is where the actual traversal takes place. The function is called recursively until no more items can be searched or a match has been found. This implementation uses a case-sensitive comparison (wstring::operator==( const wstring& )). If you need a different predicate you will have to modify the implementation as you see fit.
HTREEITEM FindItemDepthFirstImpl( HWND hwndTV, HTREEITEM htStart, const std::wstring& itemText )
{
HTREEITEM htItemMatch = NULL;
HTREEITEM htItemCurrent = htStart;
// Iterate over items until there are no more items or we found a match
while ( htItemCurrent != NULL && htItemMatch == NULL )
{
if ( GetItemText( hwndTV, htItemCurrent ) == itemText )
{
htItemMatch = htItemCurrent;
}
else
{
// Traverse into child items
htItemMatch = FindItemDepthFirstImpl( hwndTV, TreeView_GetChild( hwndTV, htItemCurrent ), itemText );
}
htItemCurrent = TreeView_GetNextSibling( hwndTV, htItemCurrent );
}
return htItemMatch;
}
The following function wraps the recursion and passes the root element as the starting point. This is the function you would call in your code. It will return an HTREEITEM if one is found, NULL otherwise.
HTREEITEM FindItem( HWND hwndTV, const std::wstring& itemText )
{
HTREEITEM htiRoot = TreeView_GetRoot( hwndTV );
return FindItemDepthFirstImpl( hwndTV, htiRoot, itemText );
}
Unfortunately there is no documented way to search a treeview by item label.
The TVM_GETISEARCHSTRING message returns the search string that the user has typed into the tree (incremental search mode) but it doesn't trigger a search or let you supply your own search string.
The only way to do it is by manually iterating the tree nodes and comparing the labels yourself. Below is an example function, beware that it is recursive and will use about half a KB of stack for every child level.
HTREEITEM TreeView_FindLabel(HWND hWnd, HTREEITEM hItemParent, LPCWSTR pszLabel)
{
TVITEM tvi;
wchar_t wchLabel[256];
for (tvi.hItem = TreeView_GetChild(hWnd, hItemParent); tvi.hItem;
tvi.hItem = TreeView_GetNextSibling(hWnd, tvi.hItem))
{
tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
tvi.pszText = wchLabel;
tvi.cchTextMax = _countof(wchLabel);
if (TreeView_GetItem(hWnd, &tvi))
{
if (_wcsicmp(tvi.pszText, pszLabel) == 0)
return tvi.hItem;
if (tvi.cChildren)
{
HTREEITEM hChildSearch = TreeView_FindLabel(hWnd, tvi.hItem, pszLabel);
if (hChildSearch) return hChildSearch;
}
}
}
return 0;
}
This is not a particularly fast way of searching the tree. If you need to do lots of searches you would be better using a std::map to keep track of the labels and tree items yourself. For example,
std::map<std::wstring, HTREEITEM> mapTreeItems;
// whenever you add an item
HTREEITEM hItem = ListView_InsertItem(...);
mapTreeItems[strLabel] = hItem;
Then you can lookup tree items by label using the map. You just have to remember to update the map and erase labels whenever an item is deleted from the tree.

MFC limit selected item in ClistCtrl

Hi
I use ClistCtrl that have 20 items and I want to limit selected item number.
for example only 10 item can be selected.
how i can do it?
thanks for your help herzl.
You would have to handle the LVN_ODSTATECHANGED notification message and count the number of selected item each time the LVIS_SELECTED state changes
Thanks
So I wrote this code. It should work. Just create an event handler for the list
void CDatenbankView::OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult)
{
int SelctedItems;
SelctedItems = 0;
int Index;
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
for (Index = 0; Index < m_List.GetItemCount(); ) //Check every Item
{
if (m_List.GetItemState (Index, LVIS_SELECTED) == LVIS_SELECTED) //Checks if it is selected
{
if (SelctedItems > 10)
{
MessageBox (_T("Cant select more than 10 Items"));
for (Index = 0; Index < m_List.GetItemCount(); )
{
m_List.SetItemState (Index, ~LVIS_SELECTED, LVIS_SELECTED);
Index++;
}
break;
}
else
{
SelctedItems++;
}
}
Index++;
}
*pResult = 0;
}
m_List is my control variable for the CListCtrl
There is no built-in functionality for such a feature. You'd have to write your our code for that. Maybe you can find another way to do it, like having a source list and a "selection list". You copy/move items from the first to the second, but you do not allow the users to put more than 10 items into the destination list.