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
In Microsoft Visual Studio 2005, create a Smart Device Pocket PC project.
Add the Win32 helper class to your project. This code is available in How to: Use a Helper Class for Platform Invokes.
Add the WinProcHooker class to your project. This code is available in How to: Use a Class for Hooking Windows Procedures.
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)
{
WndProcHooker.HookWndProc(this.Parent,
new WndProcHooker.WndProcCallback(this.WM_Notify_Handler),
Win32.WM_NOTIFY);
}
base.OnParentChanged(e);
}
// 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();
hti.pt.X = msgPos.X;
hti.pt.Y = 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 |
Win32.TVHT_ONITEMSTATEICON);
if ((hti.flags & htMask) != 0)
{
bool leftButton = (nmHdr.code == Win32.NM_CLICK);
RaiseNodeMouseClickEvent(hti.hItem,
leftButton ? MouseButtons.Left : MouseButtons.Right,
msgPos);
return 0;
}
break;
default:
break;
}
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(
tn,
button,
1, coords.X, coords.Y);
OnNodeMouseClick(e);
}
// 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;
}
}
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);
}
}
Declare a form variable named treeViewB of type TreeViewBonus.
private TreeViewBonus treeViewB;
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.
InitializeComponent();
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[] {
treeNode4,
treeNode5,
treeNode6});
treeNode3.Text = "Node3";
treeNode7.Text = "Node4";
treeNode1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode2,
treeNode3,
treeNode7});
treeNode1.Text = "Node0";
treeNode12.Text = "Node11";
treeNode11.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode12});
treeNode11.Text = "Node10";
treeNode10.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode11});
treeNode10.Text = "Node9";
treeNode9.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode10});
treeNode9.Text = "Node5";
treeNode8.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode9});
treeNode8.Text = "Node1";
this.treeViewB.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode1,
treeNode8});
this.Controls.Add(treeViewB);
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();
}
Compile and run the application.
Tasks
Concepts
Other Resources