Практическое руководство. Реализация подкласса TreeView с помощью функций обратного вызова на машинном коде
Обновлен: Ноябрь 2007
В этом примере показано, как реализовать подкласс элемента управления TreeView для создания события NodeMouseClick, которое недоступно в .NET Compact Framework.
Форма отображает созданный элемент управления TreeView и сведения о выбранном узле. На форме отображается текст узла и координаты x и y, по которым пользователь щелкнул элемент управления TreeView и которые изменяются при щелчке в другом месте узла.
Этот пример подробно описан в разделе Создание подклассов элементов управления при помощи управляемой процедуры окна.
Реализация подкласса элемента управления TreeView для создания события NodeMouseClick
В Microsoft Visual Studio 2005 создайте проект для интеллектуальных устройств.
Добавьте в проект вспомогательный класс 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); } }
Объявите переменную формы с именем treeViewB типа TreeViewBonus
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(); }
Скомпилируйте и запустите приложение.
См. также
Задачи
Пошаговое руководство. Использование класса для подключения процедур Windows
Практическое руководство. Использование вспомогательного класса для вызова неуправляемого кода
Пошаговое руководство. Отображение градиентной заливки
Основные понятия
Разделы руководства по платформе .NET Compact Framework