0

我正在尝试从 TreeView 中捕获 TVN_SELCHANGING 消息。我知道还有 BeforeSelect 事件,但我想了解为什么我无法捕捉到消息……</p>

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

我已经尝试了所有操作,但它们似乎都不起作用。我究竟做错了什么?

4

1 回答 1

2

对,这行不通。它背后有很多历史,本机 Windows 控件旨在用于 C 程序。使用 Petzold 的“Programming Windows”样式编码,您可以将窗口的自定义逻辑放在窗口过程中。并且只是按原样使用了像 TreeView 这样的控件。因此,这些控件将它们的通知消息发送到窗口。因为那是你放置代码的地方。

这与现代 GUI 代码的编写方式不太兼容。特别是继承控件以赋予其新行为的概念。就像您对 TreeViewEx 类所做的那样。你真的很想先在你自己的课堂上收到这些通知。所以你可以用 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);
}
于 2013-01-09T18:26:38.413 回答