如何:使用本机回调创建 TreeView 的子类
更新:2007 年 11 月
此示例演示如何创建 TreeView 控件的子类以创建 NodeMouseClick 事件,.NET Compact Framework 中未提供该事件。
该窗体显示 TreeView 的子类控件和关于所单击节点的信息。它显示节点的文本和 TreeView 控件被单击的位置的 x 和 y 坐标,该坐标随节点上的单击位置的改变而改变。
此示例程序在 使用托管窗口过程设置控件的子类 中有详细的描述。
创建 TreeView 控件的子类以创建 NodeMouseClick 事件
在 Microsoft Visual Studio 2005 中,创建一个智能设备 Pocket PC 项目。
将 Win32 帮助器类添加到项目中。这些代码可在 如何:将帮助器类用于平台调用 中获得。
将 WinProcHooker 类添加到项目中。这些代码可在 如何:使用类来挂钩 Windows 过程 中获得。
将 TreeViewBonus 类添加到项目中。
// 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; } }
将 TreeNodeMouseClickEventArgs 类添加到项目中。
// 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); } }
声明一个 TreeViewBonus 类型的名为 treeViewB 的窗体变量。
private TreeViewBonus treeViewB;
将下面的代码添加到 Form1 类的构造函数中,代码应放在对 InitializeComponent 的调用的后面。该代码还添加一系列树节点并排列它们的层次结构。
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);
将处理派生的 NodeMouseClick 事件的委托和事件处理方法添加到 Form1 类中。
// 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(); }
编译并运行应用程序。