How to: Subclass a TreeView by Using Native Callbacks
Important This document may not represent best practices for current development, links to downloads and other resources may no longer be valid. Current recommended version can be found here. ArchiveDisclaimer

How to: Subclass a TreeView by Using Native Callbacks

This example shows how to subclass a TreeView control to create a NodeMouseClick event, which is not available in the .NET Compact Framework.

The form displays the subclassed TreeView control and information about a clicked node. It shows the a node's text and the x and y coordinates of where the TreeView control was clicked, which changes as you click in different places on a node.

This example program is described in detail in Subclassing Controls with a Managed Window Procedure.

To subclass a TreeView control to create a NodeMouseClick event

  1. In Microsoft Visual Studio 2005, create a Smart Device Pocket PC project.

  2. Add the Win32 helper class to your project. This code is available in How to: Use a Helper Class for Platform Invokes.

  3. Add the WinProcHooker class to your project. This code is available in How to: Use a Class for Hooking Windows Procedures.

  4. Add the TreeViewBonus class to your project.

    // Extends the standard TreeView control to add an implementation
    // of the NodeMouseClick event.
    public class TreeViewBonus : TreeView
        // The original parent of this control.
        Control prevParent = null;
        // Creates a new instance of the derived TreeView control
        public TreeViewBonus()
        // Called when the control's parent is changed. Here we hook into that
        // parent's WndProc and spy on the WM_NOTIFY message. When the parent
        // changes, we unhook the old parent's WndProc and hook into the new one.
        protected override void OnParentChanged(EventArgs e)
            // unhook the old parent
            if (this.prevParent != null)
                WndProcHooker.UnhookWndProc(prevParent, Win32.WM_NOTIFY);
            // update the previous parent
            prevParent = this.Parent;
            // hook up the new parent
            if (this.Parent != null)
                new WndProcHooker.WndProcCallback(this.WM_Notify_Handler),
        // Occurs when the user clicks a TreeNode with the mouse.
        public event TreeNodeMouseClickEventHandler NodeMouseClick;
        // Occurs when the mouse pointer is over the control and a mouse button is clicked.
        protected void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
            if (NodeMouseClick != null)
                NodeMouseClick(this, e);
        // The method that gets called when a WM_NOTIFY message is received by the
        // TreeView's parent.
        // hwnd - The handle of the window that received the message
        // msg - The message received
        // wParam - The wParam arguments for the message
        // lParam - The lParam arguments for the message
        // handled - Set to true to indicate that this message was handled
        // Returns an appropriate return code for the message handled
        int WM_Notify_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            ref bool handled)
            Win32.NMHDR nmHdr = new Win32.NMHDR();
            System.Runtime.InteropServices.Marshal.PtrToStructure((IntPtr)lParam, nmHdr);
            switch (nmHdr.code)
                case Win32.NM_RCLICK:
                case Win32.NM_CLICK:
                    // get the cursor coordinates on the client
                    Point msgPos = Win32.LParamToPoint((int)Win32.GetMessagePos());
                    msgPos = this.PointToClient(msgPos);
                    // check to see if the click was on an item
                    Win32.TVHITTESTINFO hti = new Win32.TVHITTESTINFO();
           = msgPos.X;
           = msgPos.Y;
                    int hitem = Win32.SendMessage(this.Handle, Win32.TVM_HITTEST, 0, ref hti);
                    uint htMask = (
                        Win32.TVHT_ONITEMICON |
                        Win32.TVHT_ONITEMLABEL |
                        Win32.TVHT_ONITEMINDENT |
                        Win32.TVHT_ONITEMBUTTON |
                        Win32.TVHT_ONITEMRIGHT |
                    if ((hti.flags & htMask) != 0)
                        bool leftButton = (nmHdr.code == Win32.NM_CLICK);
                            leftButton ? MouseButtons.Left : MouseButtons.Right,
                        return 0;
            return 0;
        // Raises the TreeNodeMouseClick event for the TreeNode with the specified handle.
        // hNode - The handle of the node for which the event is raised
        // button - The [mouse] buttons that were pressed to raise the event
        // coords - The [client] cursor coordinates at the time of the event
        void RaiseNodeMouseClickEvent(IntPtr hNode, MouseButtons button, Point coords)
            TreeNode tn = FindTreeNodeFromHandle(this.Nodes, hNode);
            TreeNodeMouseClickEventArgs e = new TreeNodeMouseClickEventArgs(
                1, coords.X, coords.Y);
        // Finds a TreeNode in the provided TreeNodeCollection that has the handle specified.
        // Warning: recursion!
        // tnc - The TreeNodeCollection to search
        // handle - The handle of the TreeNode to find in the collection
        // Returns tThe TreeNode if found; null otherwise
        TreeNode FindTreeNodeFromHandle(TreeNodeCollection tnc, IntPtr handle)
            foreach (TreeNode tn in tnc)
            if (tn.Handle == handle) return tn;
                // we couldn't have clicked on a child of this node if this node
                // is not expanded!
                if (tn.IsExpanded)
                    TreeNode tn2 = FindTreeNodeFromHandle(tn.Nodes, handle);
                    if (tn2 != null) return tn2;
            return null;
  5. Add the TreeNodeMouseClickEventArgs class to your project.

    // Provides data for the System.Windows.Forms.TreeView.NodeMouseClick event
    public class TreeNodeMouseClickEventArgs : MouseEventArgs
        // Initializes a new instance of the TreeNodeMouseClickEventArgs class.
        // node - The node that was clicked
        // button - One of the System.Windows.Forms.MouseButtons members
        // clicks - The number of clicks that occurred
        // x - The x-coordinate where the click occurred
        // y - The y-coordinate where the click occurred
        public TreeNodeMouseClickEventArgs(TreeNode node, MouseButtons button, int clicks, int x, int y)
            base(button, clicks, x, y, 0)
            nodeValue = node;
        // Gets the node that was clicked.
        public TreeNode Node
            get { return nodeValue; }
            set { nodeValue = value; }
        TreeNode nodeValue;
        public override string ToString()
            return string.Format(
                "TreeNodeMouseClickEventArgs\r\n\tNode: {0}\r\n\tButton: {1}\r\n\tX: {2}\r\n\tY: {3}",
                nodeValue.Text, Button.ToString(), X, Y);
  6. Declare a form variable named treeViewB of type TreeViewBonus.

    private TreeViewBonus treeViewB;
  7. Add the following code to the constructor of the Form1 class, which should follow the call to InitializeComponent. This code also adds a range of tree nodes and arranges their hierarchy.

    this.treeViewB = new TreeViewBonus();
    this.treeViewB.NodeMouseClick += new TreeNodeMouseClickEventHandler(this.tv_NodeMouseClicked);
    this.treeViewB.Location = new System.Drawing.Point(18, 16);
    this.treeViewB.Size = new System.Drawing.Size(205, 144);
    this.treeViewB.Name = "treeViewB";
    // Set up the tree nodes.
    System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("Node0");
    System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("Node2");
    System.Windows.Forms.TreeNode treeNode3 = new System.Windows.Forms.TreeNode("Node3");
    System.Windows.Forms.TreeNode treeNode4 = new System.Windows.Forms.TreeNode("Node6");
    System.Windows.Forms.TreeNode treeNode5 = new System.Windows.Forms.TreeNode("Node7");
    System.Windows.Forms.TreeNode treeNode6 = new System.Windows.Forms.TreeNode("Node8");
    System.Windows.Forms.TreeNode treeNode7 = new System.Windows.Forms.TreeNode("Node4");
    System.Windows.Forms.TreeNode treeNode8 = new System.Windows.Forms.TreeNode("Node1");
    System.Windows.Forms.TreeNode treeNode9 = new System.Windows.Forms.TreeNode("Node5");
    System.Windows.Forms.TreeNode treeNode10 = new System.Windows.Forms.TreeNode("Node9");
    System.Windows.Forms.TreeNode treeNode11 = new System.Windows.Forms.TreeNode("Node10");
    System.Windows.Forms.TreeNode treeNode12 = new System.Windows.Forms.TreeNode("Node11");
    treeNode2.Text = "Node2";
    treeNode4.Text = "Node6";
    treeNode5.Text = "Node7";
    treeNode6.Text = "Node8";
    treeNode3.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
    treeNode3.Text = "Node3";
    treeNode7.Text = "Node4";
    treeNode1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
    treeNode1.Text = "Node0";
    treeNode12.Text = "Node11";
    treeNode11.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
    treeNode11.Text = "Node10";
    treeNode10.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
    treeNode10.Text = "Node9";
    treeNode9.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
    treeNode9.Text = "Node5";
    treeNode8.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
    treeNode8.Text = "Node1";
    this.treeViewB.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
  8. Add the delegate and the event-handling method for the derived NodeMouseClick event to the Form1 class.

        // Delegate represents the method that will handle
        // the NodeMouseClick event of a TreeView.
        // Parameters:
        // sender - The source of the event.
        // e - A TreeNodeMouseClickEventArgs that contains the event data.
        public delegate void TreeNodeMouseClickEventHandler(object sender, TreeNodeMouseClickEventArgs e);
    private void tv_NodeMouseClicked(object sender, TreeNodeMouseClickEventArgs e)
        // Show the current node and the coordinates
        // in TreeView control where it was clicked.
        // This is just some of the information you
        // can obtain from TreeNodeMouseClickEventArgs.
        // Use a StringBuilder for efficient
        // use of device resources.
        StringBuilder sb = new StringBuilder();
        sb.Append(e.Node.Text + " ");
        sb.Append("X: " + e.X.ToString() + ", ");
        sb.Append("Y: " + e.Y.ToString());
        label1.Text = sb.ToString();
  9. Compile and run the application.

See Also

© 2015 Microsoft