Introduction
I'm working on a project manager type of application with a pretty straightforward design: projects and their constituent steps are read from the database and displayed hierarchically as nodes in a TreeView
control. Clicking on a project or step node retrieves the details and generates a tab in an adjoining TabControl
. This was being handled by intercepting the tree's NodeMouseClick
event and was working great.
The problem was that expanding or collapsing a project node also triggered NodeMouseClick
. I needed to figure out how to perform my action when the node was actually clicked, and not when the expand / collapse bullet next to the node was clicked.
The blindingly obvious solution
I spent a while searching the web and trying the offered solutions. All were along the lines of "intercept an earlier event, test to see what happened, then set a global variable that will be read in a later event." None of these solutions could be adapted to what I needed. So I started poking around and found the solution in the event's own parameter.
NodeMouseClick
receives TreeNodeMouseClickEventArgs
. This class contains Node
, which gives the tree node that was clicked, and Location
, which gives the mouse's position relative to the tree's client space when the click occurred. Tree nodes have a property, Bounds
, which gives the node's rectangle coordinates relative to the tree view. A quick test verified that the expand / collapse bullet is outside the node's boundary. So I added
e.Node.Bounds.Contains(e.Location)
to my button test in the event and
voila! I could perform the action only when the node was really clicked, and not when its collapse state was changing. In VB, this looks like:
Private Sub CurrentTree_NodeMouseClick(ByVal sender As Object, _
ByVal e As TreeNodeMouseClickEventArgs) _
Handles CurrentTree.NodeMouseClick
If e.Node.Bounds.Contains(e.Location) AndAlso e.Button = Windows.Forms.MouseButtons.Left Then
End If
End Sub
In C#:
void CurrentTree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Node.Bounds.Contains(e.Location) && e.Button == MouseButtons.Left)
{
}
}
Works like a charm, at least with .Net 3.5. I don't expect it would be different with any other flavors of the Framework.
Update 1: Made some minor grammar changes, clarified a few points, and added a C# example. I mostly use VB, so please let me know if the C# code is off.