I've gotten to the point where an image appears on my tree view, but it is only the second one I load - it doesn't change as expected. Here are some code fragments:
MainDialog::MainDialog()
{
m_TreeImageList.Create(16, 16, ILC_COLOR, 2);
m_TreeImageList.AddBitmap(m_TreeImageList.LoadBitMap(GetModuleHandleA(0), "ClosedFolder", 16));
m_TreeImageList.AddBitMap(m_TreeImageList.LoadBitMap(GetModuleHandleA(0), "OpenFolder", 16)); // THIS IS THE ONLY ONE I SEE
return;
}
...
MainDialog::OnInitDialog(),int
{
unsigned int root1, root2;
unsigned int child1;
/* Initialize any controls here */
CTreeView *tree = GetControl(DLG_TREEVIEW);
//insert items in the treeview
root1 = tree->InsertItem("Item 1",0);
child1 = tree->InsertItem("child 1",root1);
tree->InsertItem("Child of child1",child1);
tree->InsertItem("child 2",root1);
root2 = tree->InsertItem("Item 2",0);
tree->InsertItem("child 1",root2);
tree->InsertItem("child 2",root2);
SendMessage(tree->m_hwnd,TVM_SETIMAGELIST,TVSIL_NORMAL,m_TreeImageList.m_himl);
CenterWindow();
return true;
}
The TreeView class needs a few more methods to use the image list. Specifically InsertItem doesn't set the index into the image list.
You can do it with SendMessage though. Derive a class from CTreeView, add the method below. Or just use it directly. You will need the handle to the item you want to set the image index to. It is returned when you call the InsertItem method.
The index is the zero based index into the image list.
struct TV_ITEM
{
UINT mask;
UINT hItem;
UINT state;
UINT stateMask;
POINTER pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
UINT lParam;
}
#define TV_FIRST 0x1100
#define TVIF_IMAGE 0x0002
#define TVM_SETITEMA (TV_FIRST + 13)
MyTreeView::SetItemImage(handle as unsigned int,index as int),unsigned int
{
TV_ITEM tvi;
IF(m_hWnd)
{
ZeroMemory(tvi,LEN(TV_ITEM));
tvi.mask = TVIF_IMAGE;
tvi.pszText = NULL;
tvi.cchTextMax = 0;
tvi.hitem = handle;
tvi.iImage = index
RETURN SendMessageA(m_hWnd,TVM_SETITEMA,0,&tvi);
}
RETURN 0;
}
Thanks. I now have images. It wasn't easy. The tree control doesn't want to do anything automatically.
I now want to change the image to an open folder when the item is expanded, but I'm not able to figure out how to detect the item that was.
I'm trying this:
TreeDialog::OnNotify(int code, int nID, NMHDR *pnmhdr), int
{
select code
{
case TVN_ITEMEXPANDED:
NMTREEVIEW *pTreeView = *(NMTREEVIEW)pnmhdr;
if pTreeView->action == 2
{
MessageBox(this,"Item expanded","",0);
}
}
return 0;
}
Thanks in advance.
Give me a bit and I will figure it out ;)
OK here is a modifiction of the treeview sample that comes with Aurora. I got you as far as using the notification and retrieving the handle to the item that is expanded or collapsed. Send that handle to the SetItemImage method I gave you before and you should be all set.
#define TREEVIEW_1 1
#define STATIC_2 2
#define STATIC_3 3
struct TVITEM
{
UINT mask;
UINT hItem;
UINT state;
UINT stateMask;
POINTER pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
UINT lParam;
}
struct NMTREEVIEW {
NMHDR hdr;
UINT action;
TVITEM itemOld;
TVITEM itemNew;
POINT ptDrag;
}
class TreeViewDialog:CDialog
{
declare OnInitDialog(),int;
declare OnClose(),int;
declare OnControl(int nID, int nNotifyCode, unsigned int hControl),int;
declare OnNotify(int code,int nID,NMHDR *pnmhdr),int;
}
global sub main()
{
TreeViewDialog dlg1;
dlg1.Create(0,0,300,202,0x80C80080,0,"TreeView demo",0);
dlg1.AddControl(CTTREEVIEW,"Static",32,24,107,150,0x50800007,0x200,TREEVIEW_1);
dlg1.AddControl(CTSTATIC,"Selected Item:",147,24,70,20,0x5000010B,0x0,STATIC_2);
dlg1.AddControl(CTSTATIC,"Item 1",218,24,70,20,0x50000000,0x0,STATIC_3);
dlg1.DoModal();
return 0;
}
TreeViewDialog::OnClose(),int
{
CloseDialog(1);
return true;
}
TreeViewDialog::OnInitDialog(),int
{
/* Initialize any controls here */
CTreeView *tree = GetControl(TREEVIEW_1);
//insert items in the treeview
root1 = tree->InsertItem("Item 1",0);
child1 = tree->InsertItem("child 1",root1);
tree->InsertItem("Child of child1",child1);
tree->InsertItem("child 2",root1);
root2 = tree->InsertItem("Item 2",0);
tree->InsertItem("child 1",root2);
tree->InsertItem("child 2",root2);
CenterWindow();
return true;
}
TreeViewDialog::OnControl(int nID, int nNotifyCode, unsigned int hControl),int
{
CTreeView *tree = GetControl(TREEVIEW_1);
CStatic *s = GetControl(STATIC_3);
string text;
select nID
{
case TREEVIEW_1:
select(nNotifyCode)
{
case TVNSELCHANGEDA:
hSel = tree->GetSelectedItem();
tree->GetItemText(hSel,text,255);
s->SetText(text);
}
}
return true;
}
TreeViewDialog::OnNotify(int code,int nID,NMHDR *pnmhdr),int
{
select nID
{
case TREEVIEW_1:
select(code)
{
case TVNITEMEXPANDEDA:
if(*(NMTREEVIEW)pnmhdr.action == 2)
{
MessageBox(this,"Item Expanded " + str$(*(NMTREEVIEW)pnmhdr.itemold.hitem),"");
}
else
{
MessageBox(this,"Item Collapsed " + str$(*(NMTREEVIEW)pnmhdr.itemold.hitem),"");
}
}
}
//remember to call the base class
return CDialog!!OnNotify(code,nID,pnmhdr);
}
This is better ;)
*(NMTREEVIEW)pnmhdr.itemnew.hitem
Also, I previously screwed up the struct definition for NMTREEVIEW which didn't help.
But now it works like a charm. I'll post the results here for others when it's cleaned up a bit.
Thanks, Paul.
You are most welcome. On the "little too late" front, Microsoft added an iExpandedImage field to TVITEM for Vista. It is probably part of the common control update for IE7 as well. But then it would only work for Vista or greater anyway. Handling the notification works for all windows versions.
Paul.
Here is the result of our efforts. I never have the best of luck uploading projects, but I think all of the pieces are there. Be sure to check to see if the bitmaps are loaded into the project and are named correctly. Click FILE|OPEN to display the control.
Everything is humming with my TreeView control with one nagging little bit.
I have created a context menu that works correctly and causes action on the selected item. My problem is that I must left click on an item to select it, then right click (actually anywhere in the control) to see the context menu. If I right click on an item in the tree, the selection does not change, although it is highlighted while the button is down. The highlighting returns to the previous item when the mouse button is released and the context menu appears.
Try setting the focus on right button down.
Quote from: Paul Turley on January 15, 2007, 12:48:47 PM
Try setting the focus on right button down.
I couldn't get the item to accept the focus, but this got me thinking that what we want is to make the right click select the item in the tree. After spending a lovely afternoon with MSDN, I came up with this:
TreeDialog::OnControl(int nID, int nNotifyCode, unsigned int hControl), int
{
point location;
TVHITTESTINFO *hitInfo;
CONST TVHT_ONITEMICON = 0x2;
CONST TVHT_ONITEMLABEL = 0x4;
CONST TVM_HITTEST = 4369;
select nID
{
case TD_tv_Tree:
if(nNotifyCode == NM_RCLICK)
{
hitInfo = new(TVHITTESTINFO,1);
GetCursorPos(location);
ScreenToClient(this->m_hwnd,location);
hitInfo->pt.x = location.x - 46; // adjust for location of control in dialog
hitInfo->pt.y = location.y - 56;
hitInfo->flags = TVHT_ONITEMLABEL | TVHT_ONITEMICON;
CTreeView *pTree = GetControl(TD_tv_Tree);
SendMessage(pTree->m_hwnd,TVM_HITTEST,0,hitInfo);
if(hitInfo->hItem <> null)
{
pTree->SelectItem(hitInfo->hItem);
ShowContextMenu(m.m_hMenu, location.x, location.y);
}
Delete(hitInfo);
}
}
}
You should be able to call ScreenToClient on the hitInfo->pt structure.
Quote from: Paul Turley on January 15, 2007, 08:11:29 PM
You should be able to call ScreenToClient on the hitInfo->pt structure.
Nice touch.
ScreenToClient(pTree->m_hwnd,hitInfo->pt);
TreeDialog::OnControl(int nID, int nNotifyCode, unsigned int hControl), int
{
point location;
TVHITTESTINFO *hitInfo;
CONST TVHT_ONITEMICON = 0x2;
CONST TVHT_ONITEMLABEL= 0x4;
CONST TVM_HITTEST = 4369;
select nID
{
case TD_tv_Tree:
if(nNotifyCode == NM_RCLICK)
{
CTreeView *pTree = GetControl(TD_tv_Tree);
hitInfo = new(TVHITTESTINFO,1);
GetCursorPos(location);
hitInfo->pt = location;
ScreenToClient(this->m_hwnd,location); // adjust for dialog
ScreenToClient(pTree->m_hwnd,hitInfo->pt); // adjust for control
hitInfo->flags = TVHT_ONITEMLABEL | TVHT_ONITEMICON;
SendMessage(pTree->m_hwnd,TVM_HITTEST,0,hitInfo);
if(hitInfo->hItem <> null) // hit an item
{
pTree->SelectItem(hitInfo->hItem);
ShowContextMenu(m.m_hMenu, location.x, location.y);
}
else // didn't hit an item
{
ShowContextMenu(m2.m_hMenu, location.x, location.y);
}
Delete(hitInfo);
}
}
}
Thats better ;). It is almost the same code that I use for right clicking on items in the resource view of the IDE.
Quote from: Paul Turley on January 16, 2007, 06:19:57 AM
Thats better ;). It is almost the same code that I use for right clicking on items in the resource view of the IDE.
Feel free to use any efficiencies I have found. ;)
Here is the code to date. A final product, if I get one, will be part of the Programmers Toolkit.
Things have been humming with my tree view. Additions, deletions, and edits are all fairly simple, especially now that I have replaced the array with a linked list. However, trying to get the control to drag and drop is driving me crazy :P . There appears to be comands in the CImageList class that might help, but I am getting nowhere fast. Any advice on how to approach this would be appreciated.
I have been working, on and off, for over a month on the tree control and have not been able to successfully implement the drag part.
If anyone can share code to do this in Aurora, EBasic, or IBasic, I would sure appreciate it.
EDIT: Never mind. I finally got it to work. I found an assembly language program on the net that was easy to convert to Aurora. 8)