Friday, September 4, 2009

Using Right-Click With Treeview Nodes

Lets say you have a treeview and you want a user to be able to right-click a node and then be able to either delete it, copy it, or view details about it. How do you trap the right-click event? How do you show a context menu? And how do you know what node was clicked?

There's no OnRightClick event for Treeviews- that would have been the easiest solution. There are MouseUp and MouseDown events that know what mouse button was pushed- maybe that would help.

It turns out the secret is MouseUp. The MouseUp event fires after the node click has been processed. That means when the MouseUpevent fires the node being clicked has become the selected node. So we'll use the MouseUp event, check if it was the right mouse button that was clicked, and then do whatever we need to do to the currently selected node. (Thanks to Kirby L. Wallace in TreeView Right-Click Context Menus in Access for the suggestion to use the MouseUp event.)

Now how to display the context menu. Turns out this is really easy- just use a real context menu. Access will automatically place it according to your mouse position. Best way to create context menus (AKA pop-up menus) IMO, is in code. Sample code follows for all the necessary pieces!

Here's code for the subroutines that will be called from the context (popup) menu. This code goes in a module. The code has been simplified to only work with one treeview in one form for the sake of simplicity.

Public Sub PopUpNodeDetails()
  With Forms!frmTreeviewDemo.tvDetails.SelectedItem
    MsgBox "Text=" & .Text & vbCrLf & _
      "Key=" & .Key & vbCrLf & _
      "Tag=" & .Tag
  End With
End Sub

Public Sub PopUpNodeDelete()
  If MsgBox("Delete node " & Forms!frmTreeviewDemo.tvDetails.SelectedItem.Text & "?", _
    Buttons:=vbOKCancel) = vbOK Then

    MsgBox "Node to be deleted"

    ' delete code goes here
    ' usually as well as deleting the node there's some data to be deleted from tables as well
  Else
    MsgBox "Delete cancelled"
  End If

End Sub

Now let's look at creating the popup menu. It's pretty easy really. Here's the code. It's clear enough I think that I won't go through it line by line. This code goes in a module and needs to be run to set up the popup menu. It doesn't need to be run every time the database is opened- the popup menu will be saved with your database.

Public Sub SetUpContextMenu()
  ' requires a reference to the Microsoft office object library

  On Error Resume Next ' ignore error if command bar does not exist to be deleted
  CommandBars("MyTreeviewContextMenu").Delete
  On Error GoTo 0

  With CommandBars.Add(Name:="MyTreeviewContextMenu", Position:=msoBarPopup)
    With .Controls.Add(Type:=msoControlButton)
      .Caption = "Node Details"
      .OnAction = "PopUpNodeDetails"
    End With
    With .Controls.Add(Type:=msoControlButton)
      .Caption = "Delete Node"
      .OnAction = "PopUpNodeDelete"
    End With
  End With
End Sub

Which leave only one thing- making the popup menu pop up when the right mouse button is clicked. Here's that code

Private Sub tvDetails_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal x As Long, ByVal y As Long)

  If Button = acRightButton Then
    CommandBars("MyTreeviewContextMenu").ShowPopup
  End If
End Sub
As mentioned above- it relies on the fact that the node being clicked has just become the selected node (the SelectedItem property of the treeview). It just tells the context menu we created above to pop up, and it does!

10 comments:

geckler said...

Hi
I tried your code (Access 2007) but ...
The tree and the context menu (by right-click) is displayed but the routines are not being called.
It seems that the itms in the popup is not regarded as CommandButtons.
Can you help, please?

CODE:
Public Sub treeview_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Long, ByVal y As Long)
Set nodSelected = Me.TreeView.SelectedItem
If Button = acRightButton Then
CommandBars("MyTreeviewContextMenu").ShowPopup
End If

End Sub

Public Sub PopUpNodeNew()
MsgBox "New"
With Forms!frm_new_domain.TreeView.SelectedItem
MsgBox "Text=" & .Text & vbCrLf & _
"Key=" & .Key & vbCrLf & _
"Tag=" & .Tag
End With
End Sub
Public Sub PopUpNodeEdit()
MsgBox "Edit"
With Forms!frm_new_domain.TreeView.SelectedItem
MsgBox "Text=" & .Text & vbCrLf & _
"Key=" & .Key & vbCrLf & _
"Tag=" & .Tag
End With
End Sub

Public Sub PopUpNodeDelete()
MsgBox "delete"
If MsgBox("Delete node " & Forms!frm_new_domain.TreeView.SelectedItem.Text & "?", _
Buttons:=vbOKCancel) = vbOK Then

MsgBox "Node to be deleted"

' delete code goes here
' usually as well as deleting the node there's some data to be deleted from tables as well
Else
MsgBox "Delete cancelled"
End If
End Sub

Stephen said...

Geckler- it's in the code that creates your pop-up menu that you tell the button what code to run. Did you do that?

geckler said...

Stephen,

Thanks for your quick answer.

I'm fairly new to this form of coding and therefore I'm not sure how to tell the button what code to run.

/lars (Denmark)

Geckler said...

Hi again,

I hope you can give me a tip on how to do this :-)

/lars

Geckler said...

IT WORKS :-)

Stephen said...

Geckler: congrats!

Warren said...

Hi geckler,

I have the same problem, how did you fix it?

thanks
Warren

Mark said...

Hi There

How would I indicate that the event has been "handled" to the application in VBA? I keep on getting a default context menu--I'm using a listbox.

Thanks!

Joe Robeen said...

Stephen-
Been trying to figure out a way to display a visual representation of a standard language library to automate preparation of canned reports. Everything I tried was klugey (sp?) until I found your website. With help i found here I was able to develop a three-level treeview display that shows reports with each reports sections and under each section the text options available fore each section. It looks great and should in production use soon. Thank you for organizing this rare to find subject matter into something a junior programmer like me could understand. Kudos!

Adelson R. M. Silva said...

Muito bom, parabens !