c#中的TVN_SELCHANGING消息

本文关键字:SELCHANGING 消息 TVN 中的 | 更新日期: 2023-09-27 17:51:03

我试图从TreeView捕获TVN_SELCHANGING消息。我知道还有beforeelect事件,但我想了解为什么我无法捕获消息…

我在msdn上读到TVN_SELCHANG(ED)(ING) LParam是一个指向NMTREEVIEW结构的指针。此外,代码以WM_NOTIFY消息的形式发送。

所以我试着实现它:(这对我很有帮助)

public partial class TreeviewEx : TreeView
{
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct TVITEM
    {
        public uint mask;
        public IntPtr hItem;
        public uint state;
        public uint stateMask;
        public IntPtr pszText;
        public int cchTextMax;
        public int iImage;
        public int iSelectedImage;
        public int cChildren;
        public IntPtr lParam;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct NMHDR
    {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public int code;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct NMTREEVIEW
    {
        public NMHDR hdr;
        public int action;
        public TVITEM itemOld;
        public TVITEM itemNew;
        public POINT ptDrag;
    }
    private const int TVN_FIRST = -400;
    private const int TVN_SELCHANGINGA = (TVN_FIRST - 1);
    private const int TVN_SELCHANGINGW = (TVN_FIRST - 50);
    private const int TVN_SELCHANGEDA = (TVN_FIRST - 2);
    private const int TVN_SELCHANGEDW = (TVN_FIRST - 51);
    private const int WM_NOTIFY = 0x004e;
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_NOTIFY)
        {
            var notify = (NMTREEVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMTREEVIEW));
            if (notify.action == TVN_SELCHANGINGA)
            {
                MessageBox.Show("changing");
            }
        }
        base.WndProc(ref m);
    }

我已经尝试了所有的行动,但似乎没有一个工作。我做错了什么?

c#中的TVN_SELCHANGING消息

对,这不起作用。在它背后有很多历史,本机Windows控件是为C程序设计的。使用Petzold的"编程窗口"风格的编码,将窗口的自定义逻辑放在窗口过程中。只是使用了TreeView as-is这样的控件。相应地,这些控件将它们的通知消息发送到窗口。因为那是你放代码的地方。

这与现代GUI代码的编写方式不太兼容。特别是继承控件以赋予其新行为的概念。就像你在treeviex类中做的那样。你希望首先在自己的类中获得这些通知。因此,您可以使用OnBeforeSelect()做一些有趣的事情来定制控件的行为。现在将此消息发送给父控件是一个相当大的问题,控件永远不应该知道其父控件的实现。

Winforms修复了这个问题,它反映了从父窗口返回到原始窗口的消息。必要时修改消息,以便完全清楚它是一个反射消息。它通过向消息号WM_REFLECT添加一个常量来实现这一点,您可以将该值硬编码为0x2000。所以像这样修改:

private const int WM_REFLECT = 0x2000;
protected override void WndProc(ref Message m) {
    if (m.Msg == WM_REFLECT + WM_NOTIFY) {
        var nmhdr = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
        if (nmhdr.code == TVN_SELCHANGINGW) {
           var notify = (NMTREEVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMTREEVIEW));
           // etc..
        }
    }
    base.WndProc(ref m);
}