VB6: Determine XY coords of treeview node
Over in the Classic VB forum I posted some code to enable a context menu for treeview nodes using the keyboard RightClick button. (Located to the right of the spacebar on the keyboard.)
The problem is that I wanted the context menu to appear on top of the selected node, regardless of where the mouse is. The only solution I could come up with was to manually calculate the XY coords of the selected item.
The complete code is reposted below. Note that there is room for a whole lot of improvement. Can we get rid of those nasty constants? Can we handle if the treeview is scrolled to the right? I invite all suggestions for improvement.
The following code goes into the form's module:
Code:
Private Sub Treeview1_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
If Button = vbRightButton Then ContextMenu
End Sub
Private Sub Treeview1_KeyUp(KeyCode As Integer, Shift As Integer)
If KeyCode = 93 Then ContextMenu False
End Sub
' Note that the form the treeview is on needs to have the same
' font as the treeview. If this is an issue, set the form font at the
' beginning of this function and then set it back on exit.
Public Sub ContextMenu(Optional ByVal pblnUseMouseCoords As Boolean = True)
Const RowMargin = 3 ' May need to trial & error this value if font changes
Const NodeIndent = 19 ' May need to trial & error this one as well
Dim nod As MSComctlLib.Node
Dim lngRows As Long
Dim lngRowHeight As Long
Dim lngIndex As Long
Dim lngLevel As Long
Dim x As Long
Dim y As Long
With Me
If pblnUseMouseCoords Then
' Change mnuMain(6) to the name of your menu
.PopupMenu .mnuMain(6)
Else ' Determine coords of selected node manually
' Basic height of a node is the height of the font plus the margin
lngRowHeight = .TextHeight("X") + (RowMargin * Screen.TwipsPerPixelY)
' Determine how many visible nodes precede the selected one
With .Treeview1
' If no node selected show context menu in upper left of treeview
If .SelectedItem Is Nothing Then
x = .Left + (NodeIndent \ 2) * Screen.TwipsPerPixelX
y = .Top + (lngRowHeight \ 2)
Else
lngRows = 0
For Each nod In .Nodes
If nod.Visible Then
lngRows = lngRows + 1
If nod.Index = .SelectedItem.Index Then Exit For
End If
Next
' Determine how deep the selected node is nested
' This fails acceptably if the treeview is scrolled to the right
lngLevel = 0
lngIndex = nod.Index
Do While Not (.Nodes(lngIndex).Parent Is Nothing)
lngLevel = lngLevel + 1
lngIndex = .Nodes(lngIndex).Parent.Index
Loop
' Calculate the coordinates
x = .Left + ((NodeIndent \ 2) + (lngLevel * (.Indentation + NodeIndent))) * Screen.TwipsPerPixelX
y = .Top + (lngRowHeight * lngRows) - (lngRowHeight \ 2)
' Clear nod object
Set nod = Nothing
End If
End With
' Change mnuMain(6) to the name of your menu
.PopupMenu .mnuMain(6), , x, y
End If
End With
End Sub
Edit: The ideal solution would be an API call that returns the selected node's current XY coords. Anyone know how to do that?