
Complete code and sample app in attached zip.
Ok, to use images we need to create an image list we do this by calling ImageList_Create and save the returned handle:
invoke ImageList_Create, 16, 16, ILC_COLOR32 or ILC_MASK, 4, 4
mov himlTree, eax
Our icons are 16x16, and only need 4 images
Then we have to load the icons from our resource section and add to the ImageList:
In the source, we do this 4 times: 1 for the apple icon, 1 for the orange icon, 1 for option on icon and 1 for option off icon
invoke LoadImage, hInst, ICON_ORANGE, IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR
push eax
invoke ImageList_AddIcon, himlTree, eax
call DestroyIcon
We do this for each icon we want to use in the Treeview. Technically, we don't have to call DestroyIcon since the system automatically frees an icon resource when no longer needed, but I like to free things when I don't need them.
We create the treeview in the WM_CREATE handler as any other control:
invoke CreateWindowEx, \
WS_EX_CLIENTEDGE, \
offset szWndTreeView, \
NULL, \
WS_CHILD or WS_VISIBLE or WS_TABSTOP or TVS_DISABLEDRAGDROP, \
5, 5, \
255, 280, \
hWin, TV_OPTIONS, \
hInst, NULL
mov hTVOptions, eax
In order to use "option" buttons, we need to tell the treeview where to get the icons from, we do that with SendMessage:
invoke SendMessage, eax, TVM_SETIMAGELIST, TVSIL_NORMAL, himlTree
There are two types if Image lists the treeview can use:
Normal - contains selected, nonselected and overlay images
State- indicate app defined item state
We are only interested in the Normal imagelist.
Now we will insert 2 Parents (Root Nodes) with 6 children each
PutFruitOnDaTree proc uses edi ebx esi
LOCAL tvis:TV_INSERTSTRUCT
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;%%%%% Options Treeveiw %%%%%
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;##### Apples parent
mov tvis.item.iImage, 0
mov tvis.item.iSelectedImage, 0
mov tvis.hParent, 0
mov tvis.hInsertAfter, TVI_SORT
mov tvis.item.imask, TVIF_TEXT\
or TVIF_IMAGE\
or TVIF_STATE
mov tvis.item.state, TVIS_BOLD or TVIS_EXPANDED
mov tvis.item.stateMask, TVIS_BOLD or TVIS_EXPANDED
mov tvis.item.pszText, offset szApples
invoke SendMessage, hTVOptions, TVM_INSERTITEM, 0, addr tvis
;##### Add dem apples
mov tvis.hParent, eax
mov tvis.item.imask, TVIF_TEXT or TVIF_IMAGE
mov tvis.item.iImage, OPTION_OFF
xor ebx, ebx
lea edi, offset APPLE_ARRAY
.while ebx <= APPLE_ARRAY_SIZE - 1
mov eax, [edi + 4 * ebx]
mov tvis.item.pszText, eax
invoke SendMessage, hTVOptions, TVM_INSERTITEM, 0, addr tvis
inc ebx
.endw
;##### Oranges
mov tvis.item.iImage, 1
mov tvis.item.iSelectedImage, 1
mov tvis.hParent, 0
mov tvis.hInsertAfter, TVI_SORT
mov tvis.item.imask, TVIF_TEXT\
or TVIF_IMAGE\
or TVIF_STATE
mov tvis.item.state, TVIS_BOLD or TVIS_EXPANDED
mov tvis.item.stateMask, TVIS_BOLD or TVIS_EXPANDED
mov tvis.item.pszText, offset szOranges
invoke SendMessage, hTVOptions, TVM_INSERTITEM, 0, addr tvis
;##### Add dem oranges
mov tvis.hParent, eax
mov tvis.item.imask, TVIF_TEXT or TVIF_IMAGE
mov tvis.item.iImage, OPTION_OFF
xor ebx, ebx
lea edi, offset ORANGE_ARRAY
.while ebx <= ORANGE_ARRAY_SIZE - 1
mov eax, [edi + 4 * ebx]
mov tvis.item.pszText, eax
invoke SendMessage, hTVOptions, TVM_INSERTITEM, 0, addr tvis
inc ebx
.endw
ret
PutFruitOnDaTree endp
Break Down:
How does the treeview know what item is a root node or child? We tell the treeview what is what by filling the TV_INSERTSTRUCT
To add the parent (root node)
mov tvis.item.iImage, 0
mov tvis.item.iSelectedImage, 0
mov tvis.hParent, 0
mov tvis.hInsertAfter, TVI_SORT
mov tvis.item.imask, TVIF_TEXT\
or TVIF_IMAGE\
or TVIF_STATE
mov tvis.item.state, TVIS_BOLD or TVIS_EXPANDED
mov tvis.item.stateMask, TVIS_BOLD or TVIS_EXPANDED
mov tvis.item.pszText, offset szApples
invoke SendMessage, hTVOptions, TVM_INSERTITEM, 0, addr tvis
.item.image is the image for the item, in this case the parent node is an apple
.item.iSelectedImage is the image to display when we select the item, in this case we want it to remain an apple
.hParent - we set to zero to inform the tree to insert as a root node
.hInsertAfter - I want the tree to sort the items so we use TVI_SORT valid options are: TVI_FIRST, TVI_LAST, TVI_ROOT and TVI_SORT
.item.imask tells the tree what members of the structure we are filling in. We are going to give it text, an image, selected image, and want to set the state of the item
.item.state I want the root node to be bold, and to automatically expand
.item.stateMask should mirror what you put for .state
.item.pszText is a pointer to the text you want displayed.
And for children:
;##### Add dem apples
mov tvis.hParent, eax
mov tvis.item.imask, TVIF_TEXT or TVIF_IMAGE
mov tvis.item.iImage, OPTION_OFF
xor ebx, ebx
lea edi, offset APPLE_ARRAY
.while ebx <= APPLE_ARRAY_SIZE - 1
mov eax, [edi + 4 * ebx]
mov tvis.item.pszText, eax
invoke SendMessage, hTVOptions, TVM_INSERTITEM, 0, addr tvis
inc ebx
.endw
When we insert a root node, its handle is returned in eax
.hParent is the handle of this childs parent (every child should have a parent)
.item.imask we don't want to use the previous mask, because we only need to set the text and image
.item.iImage is our option off icon
for the example, I created an array of pointer to some text I wanted to use for the children, I just loop through the array and add each child
I want to add another root node and children under it so I just repeat that process.
There is another member of the TV_INSERTSTRUCT that you might/will use and that is the .item.lParam. This is a DWORD value. You can store a number to associate with this item or a pointer to a string to associate with the item.
You can then use this value for a setting in a file if item is selected.
Ok, the magic of setting the "option" button to "on" happens in the WM_NOTIFY:
.elseif eax == WM_NOTIFY
cmp wParam, TV_OPTIONS
jne PassThrough
mov esi, lParam
mov ecx, (NMHDR ptr [esi]).code
cmp ecx, TVN_SELCHANGING
jne PassThrough
; Get parent of new item
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_PARENT, (NMTREEVIEW ptr [esi]).itemNew.hItem
test eax, eax
jz Done ; Parent clicked leave
; Clear option images
invoke ClearNode, eax
; Set Image to "on" for selected item
mov ecx, (NMTREEVIEW ptr [esi]).itemNew.hItem
mov tvi.imask, TVIF_IMAGE or TVIF_HANDLE or TVIF_SELECTEDIMAGE
mov tvi.hItem, ecx
mov tvi.iImage, OPTION_ON
mov tvi.iSelectedImage, OPTION_ON
invoke SendMessage, hTVOptions, TVM_SETITEM, 0, addr tvi
cmp wParam, TV_OPTIONS
jne PassThrough
Is the Notification coming from our treeview? No? Then pass the message on to the default proc:
PassThrough:
invoke DefWindowProc, hWin, uMsg, wParam, lParam
mov esi, lParam
mov ecx, (NMHDR ptr [esi]).code
cmp ecx, TVN_SELCHANGING
jne PassThrough
Here we inspect the messages coming in, we only want to intercept the message for Selection changing, if it is not the message, then pass it on.
In Notify messages lParam will contain a pointer to a NMHDR structure so we save that pointer to esi for ease of use.
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_PARENT, (NMTREEVIEW ptr [esi]).itemNew.hItem
test eax, eax
jz Done ; Parent clicked leave
If the item being selected is a parent, leave. We don't want to change the parent icon (in this sample)
invoke ClearNode, eax
"Un-select" any selected option buttons, code is in the attachment. It just loops through each child of the current parent and sets the icon to "Off"
; Set Image to "on" for selected item
mov ecx, (NMTREEVIEW ptr [esi]).itemNew.hItem
mov tvi.imask, TVIF_IMAGE or TVIF_HANDLE or TVIF_SELECTEDIMAGE
mov tvi.hItem, ecx
mov tvi.iImage, OPTION_ON
mov tvi.iSelectedImage, OPTION_ON
invoke SendMessage, hTVOptions, TVM_SETITEM, 0, addr tvi
Here we use a TVITEM structure to tell the treeview what items image we want to change and to change to the "Item On" icon.
Wow, great! But how can we find out which "child" was selected? I dunno, was hoping you knew
There is no simple way to go through a tree, for this example, I modified some code I wrote back in '03 that enumerates a trees roots, parents, and children.
Basically you:
1. Get the handle to the first parent and save it.
2. Get the handle of the parents first child and save it.
3. Loop through the rest of children under current parent.
4. Get handle to next parent and save it.
5. Get the handle of the parents first child and save it.
6. Loop through the rest of children under current parent.
7. Repeat step 4 till no more
ShowSelected proc uses ebx esi edi LOCAL mbp:MSGBOXPARAMS Local TempBuffer[20]:BYTE local tvi:TVITEM
xor esi, esi
In this, we will use esi counter for number selected
mov byte ptr [lpszTotal], 0
Place a NULL in our buffer to "clear it out"
invoke szCatStr, offset lpszTotal, offset szSelApple
This is a function from the MASM32 Library that will append the second string to the first
;##### Get handle of first root
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_ROOT, NULL
mov edi, eax
First we need to get the handle to the first root node and save it for later use.
;##### Get handle of first child
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_CHILD, eax
mov ebx, eax
Next we get the handle to the first nodes child and save it for later use
;##### See if selected
lea ecx, TempBuffer
mov byte ptr [ecx], 0
mov tvi.imask, TVIF_IMAGE or TVIF_TEXT
mov tvi.hItem, eax
mov tvi.pszText, ecx
mov tvi.cchTextMax, 20
invoke SendMessage, hTVOptions, TVM_GETITEM, 0, addr tvi
.if tvi.iImage == OPTION_ON
invoke szMultiCat, 2, offset lpszTotal, addr TempBuffer, offset szCRLF
inc esi
jmp GetOranges
.endif
We fill in a few members of the TVITEM structure telling the treeview what info we want. In this case we want the text and the index of the image in the image list. We then check the index of the image and if it is the OPTION_ON icon, we add the childs text to our buffer and then jump to GetOranges, if not, we get the next child.
.pszText is a pointer to the buffer to receive the text
.cchTextMax is the size of our buffer to receive the text
NextAppleChild:
;##### Loop through the rest of the apples
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_NEXT, ebx
.if eax == 0
invoke szCatStr, offset lpszTotal, offset szCRLF
jmp GetOranges
.else
mov ebx, eax
;##### See if selected
lea ecx, TempBuffer
mov byte ptr [ecx], 0
mov tvi.imask, TVIF_IMAGE or TVIF_TEXT
mov tvi.hItem, eax
mov tvi.pszText, ecx
mov tvi.cchTextMax, 20
invoke SendMessage, hTVOptions, TVM_GETITEM, 0, addr tvi
.if tvi.iImage == OPTION_ON
invoke szMultiCat, 2, offset lpszTotal, addr TempBuffer, addr szCRLF
inc esi
jmp GetOranges
.endif
jmp NextAppleChild
.endif
We get the next child by passing the handle of the previous child to the treeview with SendMessage, we save the returned handle for the next call to get the next child. We loop until SendMessages returns 0 checking the index of the childs image and if it is the ON icon, append the text to our buffer and jump to GetOrangs if not, get and check the next child.
The following is the exact same as above to get the selected orange, except we pass the handle of the first root in the first call to SendMessage to get the next root node.
GetOranges:
invoke szCatStr, offset lpszTotal, offset szSelOrange
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_NEXT, edi
;##### Get handle of first child
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_CHILD, eax
mov ebx, eax
;##### See if selected
lea ecx, TempBuffer
mov byte ptr [ecx], 0
mov tvi.imask, TVIF_IMAGE or TVIF_TEXT
mov tvi.hItem, eax
mov tvi.pszText, ecx
mov tvi.cchTextMax, 20
invoke SendMessage, hTVOptions, TVM_GETITEM, 0, addr tvi
.if tvi.iImage == OPTION_ON
invoke szCatStr, offset lpszTotal, addr TempBuffer
inc esi
jmp NoMoreOranges
.endif
NextOrangeChild:
;##### Loop through the rest of the apples
invoke SendMessage, hTVOptions, TVM_GETNEXTITEM, TVGN_NEXT, ebx
.if eax == 0
jmp NoMoreOranges
.else
mov ebx, eax
;##### See if selected
lea ecx, TempBuffer
mov byte ptr [ecx], 0
mov tvi.imask, TVIF_IMAGE or TVIF_TEXT
mov tvi.hItem, eax
mov tvi.pszText, ecx
mov tvi.cchTextMax, 20
invoke SendMessage, hTVOptions, TVM_GETITEM, 0, addr tvi
.if tvi.iImage == OPTION_ON
invoke szCatStr, offset lpszTotal, addr TempBuffer
inc esi
jmp NoMoreOranges
.endif
jmp NextOrangeChild
.endif
NoMoreOranges:
mov mbp.cbSize, sizeof MSGBOXPARAMS
push hMain
pop mbp.hwndOwner
push hInst
pop mbp.hInstance
mov mbp.lpszIcon, ICON_APPLE
.if esi == 0
mov mbp.lpszText, offset szNothingSelected
.else
mov mbp.lpszText, offset lpszTotal
.endif
mov mbp.lpszCaption, offset szMsgBoxCaption
mov mbp.dwStyle, MB_USERICON or MB_OK
invoke MessageBoxIndirect, addr mbp
ret
ShowSelected endp
MessageBoxIndirect is a MessageBox that can be customized, here I use the apple Icon.
Attached File(s)
-
Treeview.zip (18.37K)
Number of downloads: 169







MultiQuote


|