Handling windows notifications in derived C# user control

前端 未结 3 434
梦谈多话
梦谈多话 2021-01-27 04:07

How can I handle any of the tree view notifications listed here in a C# class that is derived from the .NET TreeView control?

I tried to handle the click notification, f

相关标签:
3条回答
  • 2021-01-27 04:58

    It is not that simple. Check the MSDN article, the NM_CLICK notification is delivered as a WM_NOTIFY message. And it is sent to the parent of the treeview. Winforms has plumbing in place to echo it back to the original control to allow the message to be handled by a class derived from TreeView and customize the event handling. That's done by adding 0x2000 to the message, the value of WM_REFLECT in the Winforms source code.

    So the code should look like this:

    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    class ExtendedTreeView : TreeView {
        protected override void WndProc(ref Message m) {
            if (m.Msg == WM_REFLECT + WM_NOFITY) {
                var notify = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
                if (notify.code == NM_CLICK) {
                    MessageBox.Show("yada");
                    m.Result = (IntPtr)1;
                    return;
                }
    
            }
            base.WndProc(ref m);
        }
        private const int NM_FIRST = 0;
        private const int NM_CLICK = NM_FIRST - 2;
        private const int WM_REFLECT = 0x2000;
        private const int WM_NOFITY = 0x004e;
    
        [StructLayout(LayoutKind.Sequential)]
        private struct NMHDR {
            public IntPtr hwndFrom;
            public IntPtr idFrom;
            public int code;
        }
    }
    

    Beware that TreeView already does all this, that's how the NodeMouseClick, Click and MouseClick events get generated. The code that does this also works around some quirks in the native control so be sure you really need this before committing to use it. Review the Reference Source if you want to know what's going on.

    0 讨论(0)
  • 2021-01-27 05:05

    Notifications are sent to the control's parent:

    Notifies the parent window of a tree-view control that the user has clicked the left mouse button within the control.

    This is done with the WM_NOITIFY message. Luckily, the authors also included a mechanism called reflection to allow sub classes of the treeview to receive the notifications too. The message is &H2000 | WM_NOTIFY which you can treat exactly as WM_NOTIFY.

    Also note that NM_CLICK is not a message, but a notification wrapped inside an NMHDR structure

    This notification code is sent in the form of a WM_NOTIFY message.

    0 讨论(0)
  • 2021-01-27 05:08

    There are 2 important things, which are mentioned in MSDN: 1) msg.lparam is pointer to NMHDR structure 2) notifications are send to parent control

    So the working code is (compile as console app - it will print messages there):

    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    class MyTreeView : TreeView
    {
        public TreeView RealTreeView;
        public MyTreeView()
        {
            RealTreeView = new TreeView();
            RealTreeView.Dock = DockStyle.Fill;
            Controls.Add(RealTreeView);
        }
        enum WM
        {
            NOTIFY = 78
        }
        enum NM : uint
        {
            FIRST = 0,
            NM_CLICK = unchecked(FIRST - 2),
            NM_CUSTOMDRAW = unchecked(FIRST - 12),
            NM_DBLCLK = unchecked(FIRST - 3),
            NM_KILLFOCUS = unchecked(FIRST - 8),
            NM_RCLICK = unchecked(FIRST - 5),
            NM_RDBLCLK = unchecked(FIRST - 6),
            NM_RETURN = unchecked(FIRST - 4),
            NM_SETCURSOR = unchecked(FIRST - 17),
            NM_SETFOCUS = unchecked(FIRST - 7)
        }
    
        [StructLayout(LayoutKind.Sequential)]
        struct NMHDR {
            public IntPtr hwndFrom;
            public UIntPtr idFrom;
            public uint code;
        }
    
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (m.Msg == (int)WM.NOTIFY)
            {
                uint code;
                unsafe
                {
                    var nmhdr = (NMHDR*)m.LParam.ToPointer();
                    code = nmhdr->code;
                }
                NM nmCode = (NM)code;
                Console.WriteLine("WM_NOTIFY " + nmCode);
            }
        }
    }
    
    public class MyGuiClass
    {
        public static void Main()
        {
            Form f = new Form();
            var tv = new MyTreeView();
            tv.RealTreeView.Nodes.Add("zero").Nodes.Add("sub-zero");
            tv.RealTreeView.Nodes.Add("one");
            tv.RealTreeView.Nodes.Add("two");
            tv.RealTreeView.Nodes.Add("three");
            tv.Dock = DockStyle.Fill;
            f.Controls.Add(tv);
            Application.Run(f);
        }
    }
    

    Edit: and don't forget to compile with /unsafe of course.

    0 讨论(0)
提交回复
热议问题