在派生的 C# 用户控件中处理窗口通知
本文关键字:处理 窗口 通知 控件 用户 派生 | 更新日期: 2023-09-27 18:34:15
从 .NET 树视图控件派生的 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 树视图控件有一个单击事件。这只是第一次测试。稍后我想启用TVS_EX_MULTISELECT
样式。由于 .NET TreeView 控件在启用 TVS_EX_MULTISELECT
时不会触发任何AfterSelect
事件,因此我想调查TVN_SELCHANGED
的行为,并在以后TVN_ITEMCHANGED
通知。
事情没那么简单。 查看 MSDN 文章,NM_CLICK通知作为WM_NOTIFY消息传递。 并将其发送到树视图的父级。 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
消息完成的。幸运的是,作者还包括一种称为反射的机制,以允许树视图的子类也接收通知。该消息&H2000 | WM_NOTIFY
,您可以将其完全视为WM_NOTIFY
。
另请注意,NM_CLICK
不是消息,而是包装在NMHDR
结构中的通知
此通知代码以WM_NOTIFY消息的形式发送。
MSDN 中提到了 2 件重要的事情: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 进行编译。