-
Jun 22nd, 2019, 12:35 PM
#1
[RESOLVED] TreeView ... get Node index from Node hItem.
Ok, I was helping out yereverluvinuncleber and ran across a problem I haven't optimally solved.
With the ListView, when you call SendMessage with LVM_HITTEST, you get the item's index returned.
However, with the TreeView, when you call SendMessage with TVM_HITTEST, you get a handle (hItem), rather than the item's index.
To further access the TreeView's node that we hit, I need the node's index (from the hItem).
Here's some code I put together to get the index from the hItem:
Code:
Option Explicit
'
Private Declare Function SendMessageA Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
'
Public Function GetTreeNodeIndexFromHandle(tre As TreeView, hNode As Long)
Dim i As Long
Dim h As Long
'
For i = 1& To tre.Nodes.Count
h = GetTreeNodeHandle(tre, tre.Nodes(i))
If h = hNode Then
GetTreeNodeIndexFromHandle = i
Exit Function
End If
Next
' Returns ZERO if we fall out.
End Function
Public Function GetTreeNodeHandle(tre As TreeView, oNode As Node) As Long
Const TVM_GETNEXTITEM As Long = &H110A&
Const TVGN_CARET As Long = &H9&
Dim selNode As Node
'
Set selNode = tre.SelectedItem
Set tre.SelectedItem = oNode
GetTreeNodeHandle = SendMessageA(tre.hWnd, TVM_GETNEXTITEM, TVGN_CARET, ByVal 0&)
Set tre.SelectedItem = selNode
End Function
It works. However, after playing with it, I've discovered that it does cause a bit of flicker in the TreeView. The obvious reason for this is that the GetTreeNodeHandle procedure is briefly changing the selected node.
I could develop some enumerative (and probably recursive) code to retrieve all the hItem values for a TreeView without changing the selection. However, that's more than a trivial amount of work. I'm just wondering if I'm overlooking something.
Thanks,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Jun 22nd, 2019, 12:51 PM
#2
Fanatic Member
Re: TreeView ... get Node index from Node hItem.
It seems that it would be less code if you use TreeView.HitTest method, which returns a Node object, which has an Index property.
-
Jun 22nd, 2019, 01:03 PM
#3
Re: TreeView ... get Node index from Node hItem.
Originally Posted by qvb6
It seems that it would be less code if you use TreeView.HitTest method, which returns a Node object, which has an Index property.
qvb6, you might be right. But now I've gotten hard-headed and I want to figure this out. There's got to be a way (through API) to get from hItem to Index.
I'm working on a recursive procedure now.
Basically, the problem is getting the hItem of any Node we choose, without tampering with the selected node. In .NET, they fixed this (by supplying node handles), but I'd like to do it in VB6.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Jun 22nd, 2019, 06:28 PM
#4
Re: TreeView ... get Node index from Node hItem.
Ok, I found some old code that did allow me to work through getting the Node object from the hItem (for the node). With this, it's trivial to get the index. This is the opposite of what I asked, but it kills two birds with one stone: 1) It eliminates the loop, and 2) It obviates the need to select the node (thereby eliminating flicker).
Here's the code:
Code:
Option Explicit
'
Private Declare Function SendMessageA Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
Private Declare Sub FillMemory Lib "kernel32" Alias "RtlFillMemory" (ByRef Dest As Any, ByVal length As Long, Optional ByVal Fill As Byte)
'
Private Type TVITEM
mask As Long
hitem As Long
state As Long
stateMask As Long
pszText As Long ' if a string, must be pre-allocated!!
cchTextMax As Long
iImage As Long
iSelectedImage As Long
cChildren As Long
lParam As Long
End Type
'
Public Function GetTreeNodeFromHandle(tre As TreeView, hNode As Long) As Node
' For both the Mscomctl.ocx and Comctl32.ocx TreeView and ListView controls,
' the Node and ListItem's ObjPtr() values reside at the 3rd DWORD
' (byte offset 8) in the Node's and ListItem's lParam.
'
Const TVIF_PARAM As Long = &H4&
Const TVM_GETITEM As Long = &H110C&
Dim tvi As TVITEM
Dim pNode As Long
Dim oNode As Node
'
tvi.hitem = hNode
tvi.mask = TVIF_PARAM
If SendMessageA(tre.hWnd, TVM_GETITEM, 0&, tvi) = 0& Then Exit Function
If tvi.lParam = 0& Then Exit Function
'
CopyMemory pNode, ByVal tvi.lParam + 8&, 4&
If pNode = 0& Then Exit Function
'
CopyMemory oNode, pNode, 4&
Set GetTreeNodeFromHandle = oNode
FillMemory oNode, 4&, CByte(0) ' Clean-up.
End Function
This one's resolved.
Thanks,
Elroy
EDIT1: And here's the inverse in case anyone needs it. This is nice in that it effectively provides a "Handle" property to our TreeView nodes:
Code:
Private Function hItemFromNode(ByVal oNode As Node) As Long
If (oNode Is Nothing) = False Then CopyMemory hItemFromNode, ByVal (ObjPtr(oNode) + 68&), 4&
End Function
Last edited by Elroy; Jun 22nd, 2019 at 06:56 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Jun 23rd, 2019, 10:18 AM
#5
Re: [RESOLVED] TreeView ... get Node index from Node hItem.
Nice!
Didn't know this and I used to loop Nodes collection and get hItem for each node like this
thinBasic Code:
Private Function pvGetNode(ByVal hItem As Long) As ComctlLib.Node
Dim lTmp As Long
For Each pvGetNode In m_oCtl.Nodes
Call CopyMemory(lTmp, ByVal UnsignedAdd(ObjPtr(pvGetNode), 68), PTR_SIZE)
If lTmp = hItem Then
Exit Function
End If
Next
End Function
TVIF_PARAM trick helps me get rid of the loop in pvGetNode
thinBasic Code:
Private Function pvGetNode(ByVal hItem As Long) As ComctlLib.Node
Dim uItem As TV_ITEM
Dim lPtr As Long
If hItem <> 0 Then
With uItem
.hItem = hItem
.mask = TVIF_PARAM
Call SendMessage(m_hWndCtl, TVM_GETITEM, 0, uItem)
If .lParam <> 0 Then
Call CopyMemory(lPtr, ByVal UnsignedAdd(.lParam, 8), PTR_SIZE)
Call vbaObjSetAddref(pvGetNode, lPtr)
End If
End With
End If
End Function
Private Function pvGetHItem(oNode As ComctlLib.Node) As Long
If Not oNode Is Nothing Then
Call CopyMemory(pvGetHItem, ByVal UnsignedAdd(ObjPtr(oNode), 68), PTR_SIZE)
End If
End Function
cheers,
</wqw>
-
Oct 9th, 2020, 02:30 AM
#6
New Member
Re: [RESOLVED] TreeView ... get Node index from Node hItem.
Registered just to say Thank you, to Elroy.
I got stuck after HitTest returns hwnd of TV Item. Can't find a way to convert hwnd to Node.
Really appreciated sharing your code.
-
Jan 22nd, 2021, 10:04 AM
#7
New Member
Re: [RESOLVED] TreeView ... get Node index from Node hItem.
In my case , the offset to the handle is 72 , not 68
CopyMemory hItemFromNode, ByVal (ObjPtr(oNode) + 72&), 4&
-
Jan 22nd, 2021, 10:42 AM
#8
Re: [RESOLVED] TreeView ... get Node index from Node hItem.
Originally Posted by Hussainsat
In my case , the offset to the handle is 72 , not 68
CopyMemory hItemFromNode, ByVal (ObjPtr(oNode) + 72&), 4&
This sucks!
I'm using this with a reference to Microsoft Windows Common Controls 5.0 (SP2) in C:\Windows\SysWOW64\COMCTL32.OCX with OCX file version 6.0.81.5
Is it with some other OCX version of Microsoft Windows Common Controls 5.0 (SP2) that this offset has been changed?
cheers,
</wqw>
-
Jan 22nd, 2021, 11:04 AM
#9
New Member
Re: [RESOLVED] TreeView ... get Node index from Node hItem.
Originally Posted by wqweto
This sucks!
I'm using this with a reference to Microsoft Windows Common Controls 5.0 (SP2) in C:\Windows\SysWOW64\COMCTL32.OCX with OCX file version 6.0.81.5
Is it with some other OCX version of Microsoft Windows Common Controls 5.0 (SP2) that this offset has been changed?
cheers,
</wqw>
My OCX is C:\Windows\SysWOW64\MSCOMCTL.OCX
Microsoft Windows Common Controls 6.0 (SP6)
I wrote some code to scan for the offset ..
Code:
Function FindOffsetToVal(Obj As Node, Val As Long) As Long
Dim Offset&, Temp&
For Offset = 0 To 100
CopyMemory Temp, ByVal (ObjPtr(Obj) + Offset), 4&
If Temp = Val Then
FindOffsetToVal = Offset
Exit For
End If
Next
End Function
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|