我试图从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);
}
我已经尝试了所有的行动,但似乎没有一个工作。我做错了什么?
对,这不起作用。在它背后有很多历史,本机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);
}