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

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.

Related

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.

TinyXML cant compare Attribute to the char

bool win::checkIfFScreen(sf::RenderWindow &window)
{
TiXmlDocument doc;
TiXmlElement * fullscreen;
if(!doc.LoadFile("videoSettings.xml"))
{
fullscreen = new TiXmlElement( "Window" );
fullscreen->SetAttribute("Fullscreen: ", 0);
doc.LinkEndChild( fullscreen );
fullscreen->Attribute("Fullscreen: ");
std::cout << typeid(*fullscreen->Attribute("Fullscreen: ")).name() << std::endl;
doc.SaveFile("videoSettings.xml");
return false;
}
if(*(fullscreen->Attribute("Fullscreen: ")) == '0')
return false;
return true;
}
Idea:
So, I wanna store information about persons preference if he wants for the game to be fullscreen or windowed. I've created this bool function that checks if there is "videoSettings.xml" file and returns information about users preference. If the file doesn't exists it will be create with Fullscreen set to 0 (which basically means that the game will be windowed and the user could change it later in the game's settings).
Part that doesn't work:
if(*(fullscreen->Attribute("Fullscreen: ")) == '0')
return false;
After adding this two lines I've got Segmentation fault (core dumped).
It seems that that value is stored as char.
EDIT:
This lines solved everything :) .
TiXmlHandle docHandle ( &doc );
TiXmlElement *child = docHandle.FirstChild( "Window" ).ToElement();
if(child)
if(*child->Attribute("fullscreen") == '1')
return true;
else if(*child->Attribute("fullscreen") == '0')
return false;
Thank you #frasnian .
Your code has this:
TiXmlElement * fullscreen; // not initialized to anything here
if(!doc.LoadFile("videoSettings.xml")) // LoadFile returns true on success
{
fullscreen = new TiXmlElement( "Window" ); // okay
...
return false;
}
// question: if doc.LoadFile() succeeds, what is this going to do-
if(*(fullscreen->Attribute("Fullscreen: ")) == '0')
return false;
You are using fullscreen before it is initialized with anything.
On edit
In response to question in comment:
If loading the document succeeds, you need to get the root element with something like:
TiXmlElement* root = doc.FirstChildElement("Whatever"); // root element name
if (root){
TiXmlElement* el = root->FirstChildElement("Window"); // etc, etc,
When you have walked the document hierarchy to wherever your "Window" element is, use TiXmlElement::Attribute() or TiXmlElement::QueryAttribute() to get the value of the attribute (if it exists).
A better approach than walking the hierarchy with FirstChild/NextSibling, etc. (inherited by TiXmlElement from TiXmlNode) is probably to use handles. See the docs for TinyXML related to TiXmlHandle - the main docs page has an example that is pretty straightforward.
As a side note, the colon after the attribute name in your posted code should be removed (i.e. "fullscreen", not "Fullscreen:").
Also, this line:
fullscreen->Attribute("Fullscreen: ");
right after you call LinkEndChild() does not do anything.

Changing color of a specific character in an item in CListCtrl in MFC

I have a CListCtrl and I need to change the color of A SPECIFIC character/set of characters (which I choose by comparison) from the text of every cell in the list.
I know how to change the color of the entire text of the cell when I find the character/set of characters (by using 'strstr' command), but I can't find an example which shows how to change ONLY the character/set of characters.
Here is a sample of my code:
void Agenda::OnCustomdrawMyList( NMHDR* pNMHDR, LRESULT* pResult )
{
NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)pNMHDR;
*pResult = CDRF_DODEFAULT;
if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW;
return;
}else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYSUBITEMDRAW;
return;
}else if ( (CDDS_SUBITEM | CDDS_ITEMPREPAINT) == pLVCD->nmcd.dwDrawStage )
{
// So right now I am in the stage where a SUBITEM is PREPAINTED
int nItem = pLVCD->nmcd.dwItemSpec;
int nSubItem = pLVCD->iSubItem;
char a[100];
listControl.GetItemText(nItem,nSubItem,a,100);
COLORREF textColorFound, textColorDefault;
textColorDefault = RGB(0,0,0);
pLVCD->clrText = textColorDefault;
char* startingFrom;
if( (startingFrom = strstr(a,filterText)) != NULL ) {
// Could I set a pointer here or something like that so
// the coloring could start only from 'startingFrom'
// and stop at 'strlen(filterText)' characters?
textColorFound = RGB(205,92,92);
pLVCD->clrText = textColorFound;
}
*pResult = CDRF_DODEFAULT;
}
}
listControl is the variable for my CListCtrl
the other things are pretty self-explanatory
No, you cannot do this. What you will have to do is custom-draw the text in question. This will be tricky because you will have to do it with two different calls, between which you will have to manually adjust the color and the drawing location to account for the intercharacter spacing etc. And you better hope that you don't need to do multi-line output.
Take a look at the article Neat Stuff to Do in List Controls Using Custom Draw by Michael Dunn on CodeProject to get some ideas on how to proceed.
Alternatively, if you can use the Toolkit Pro toolkit from CodeJock you can leverage their "XAML" support (I use quotes because it's not really XAML, but their own implementation of a subset of XAML) and let them do all the hard work.
Digging on the same issue; But I wouldn't go so far as modifying/adding to the default Windows behaviour for painting strings... apparently that would be the endpoint of having it owner-drawn.(aici am murit si eu :).

MFC ListView Change Item Image

I have added items to ListControl, they have images. Now I want to change them, I tried to do GetItem and SetItem, but I was not able. At least I don't know how to get an Item I want. How I can change Image of an item in ListView?
Thanks
P.S.
I've managed to solve it. Here is solution:
This is how to loop
LVITEMW pitem;
ZeroMemory(&pitem, sizeof(pitem));
pitem.mask = LVIF_TEXT | LVIF_IMAGE;
pitem.iItem = <SET INDEX OF YOUR ITEMS HERE, YOU CAN LOOP HERE>;
pitem.iSubItem = 0;
pitem.pszText = new wchar_t[256];
pitem.cchTextMax = 255;
mlist.GetItem(&pitem);
And after selecting an item, you can change it's image like this:
pitem.iImage = newindex;
mlist.SetItem(&pitem);
Using CListCtrl::SetItem is right. You have to set the nMask parameter to LVIF_IMAGE and provide the index of the image in der image-list in the iImage parameter.
The solution highlighted in the first post was not working for me. After a short look to Microsoft Documentation, the signature of the SetItem function is:
BOOL SetItem(const LVITEM* pItem);
pItem should be a pointer to a LVITEM which is const, which is not really the case in this situation...
However, the next solution is working for me:
// Let's say my CListCtrl is named m_listCtrl
// Loop on items of CListCtrl
for( int i = 0; i < m_listCtrl.GetItemCount(); i++ )
{
// And then you define a new image with the index iImage for the item i
m_listCtrl.SetItem(i, 0, LVIF_IMAGE, NULL,
iImage, 0, 0, 0);
}

CTreeCtrl - getting an item position

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);