在派生的C#用户控件中处理Windows通知

如何处理从.NET TreeView控件派生的C#类中列出的任何树视图通知?

我试图处理点击通知,例如,像这样:

class ExtendedTreeView : TreeView { private const Int32 NM_FIRST = (Int32)(0U - 0U); private const Int32 NM_CLICK = unchecked((Int32)((UInt32)NM_FIRST - 2U)); protected override void WndProc(ref Message m) { if (m.Msg == NM_CLICK) { MessageBox.Show("NM_CLICK"); } base.WndProc(ref m); } } 

但永远不会显示消息框。 这是我第一次尝试使用Win32 API来修改.NET控件的行为,所以我不知道出了什么问题。

这是处理这些通知的正确方法吗?

仅供参考:我知道.NET TreeView控件有一个click事件。 这只是第一次测试。 后来我想启用TVS_EX_MULTISELECT样式。 由于.NET TreeView控件在启用AfterSelect时不会触发任何AfterSelect事件,因此我想稍后调查TVN_SELCHANGEDTVN_ITEMCHANGED通知的行为。

事情并非那么简单。 检查MSDN文章 ,NM_CLICK通知作为WM_NOTIFY消息传递。 它被发送到treeview的父级 。 Winforms有适当的管道将它回显给原始控件,以允许消息由派生自TreeView的类处理并自定义事件处理。 这是通过在消息中添加0x2000,Winforms源代码中WM_REFLECT的值来完成的。

所以代码应该如下所示:

 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; } } 

请注意TreeView已经完成了所有这些,这就是NodeMouseClick,Click和MouseClick事件的生成方式。 执行此操作的代码也适用于本机控件中的一些怪癖,因此请确保在提交使用它之前确实需要它。 如果您想知道发生了什么,请查看参考源。

通知发送到控件的父级:

通知父窗口树视图控件,用户在控件中单击了鼠标左键。

这是通过WM_NOITIFY消息完成的。 幸运的是,作者还包括一种称为reflection的机制,允许树视图的子类也接收通知。 消息是&H2000 | WM_NOTIFY &H2000 | WM_NOTIFY ,您可以完全视为WM_NOTIFY

另请注意, NM_CLICK不是消息,而是包含在NMHDR结构中的通知

此通知代码以WM_NOTIFY消息的forms发送。

有两个重要的事情,在MSDN中提到:1)msg.lparam是指向NMHDR结构的指针2)通知发送到父控件

所以工作代码是(编译为控制台应用程序 – 它将在那里打印消息):

 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); } } 

编辑:当然不要忘记用/ unsafe编译。