방법: 네이티브 콜백을 사용하여 TreeView 서브클래싱

업데이트: 2007년 11월

이 예제에서는 TreeView 컨트롤을 서브클래싱하여 .NET Compact Framework에서 사용할 수 없는 NodeMouseClick 이벤트를 만드는 방법을 보여 줍니다.

폼에 서브클래싱된 TreeView 컨트롤 및 클릭한 노드에 대한 정보가 표시됩니다. 폼은 노드의 텍스트 및 TreeView 컨트롤을 클릭한 위치의 x 및 y 좌표를 보여 줍니다. 이 좌표는 노드의 다른 위치를 클릭할 때마다 변경됩니다.

이 예제 프로그램은 관리되는 Window 프로시저를 사용하여 컨트롤 서브클래싱에서 자세하게 설명됩니다.

TreeView 컨트롤을 서브클래싱하여 NodeMouseClick 이벤트를 만들려면

  1. Microsoft Visual Studio 2005에서 스마트 장치 Pocket PC 프로젝트를 만듭니다.

  2. 프로젝트에 Win32 도우미 클래스를 추가합니다. 이 코드는 방법: 플랫폼 호출에 도우미 클래스 사용에서 사용할 수 있습니다.

  3. 프로젝트에 WinProcHooker 클래스를 추가합니다. 이 코드는 방법: Windows 프로시저를 후크하기 위한 클래스 사용에서 사용할 수 있습니다.

  4. 프로젝트에 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;
        }
    }
    
  5. 프로젝트에 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);
        }
    }
    
  6. TreeViewBonus 형식의 treeViewB 폼 변수를 선언합니다.

    private TreeViewBonus treeViewB;
    
  7. 다음 코드를 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);
    
    
  8. 파생된 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();
    
    }
    
  9. 응용 프로그램을 컴파일하여 실행합니다.

참고 항목

작업

방법: Windows 프로시저를 후크하기 위한 클래스 사용

방법: 플랫폼 호출에 도우미 클래스 사용

방법: 네이티브 콜백을 사용하여 TreeView 서브클래싱

방법: 그라데이션 채우기 표시

개념

.NET Compact Framework 방법 항목

기타 리소스

.NET Compact Framework의 상호 운용성