禁用ListView报表模式下的列调整大小






3.94/5 (9投票s)
本文介绍了如何使用 NativeWindow 对象来控制 ListView 的列标题的消息处理。 这允许程序员拦截消息并阻止或响应诸如列调整大小之类的事情。
引言
我最近的任务是想办法在 ListView
控件中禁用列调整大小。 在 Google 搜索中寻找线索,阅读文档以及进行一些试验和错误后,我终于弄清楚了如何做到这一点。 解决方案很简单、优雅,并且可以用作使用相同技术处理 ListView
控件的其他事情的基础。
背景
当 ListView
控件置于报告模式时,一个附加的子窗口将添加到控件中以容纳列标题。 此子窗口的 Windows 类是 SysHeader32
类。 遗憾的是,Microsoft 似乎忽略了在 .NET Framework 中公开此控件的包装器,并且没有提供简单的访问方法。
最终,这意味着大量 Windows 消息正在发送到您的 ListView
控件的 SysHeader32
窗口,该窗口不会为您的控件触发任何事件,并且您无法响应。
解决方案
解决方案是一个三步过程
- 获取
ListView
的SysHeader32
子窗口的 Windows 句柄。 - 使用 Windows 句柄以某种方式接管其窗口的消息处理。
- 编写代码以响应感兴趣的消息以完成我们的目标。
步骤 1
第一步很简单。 还记得 Win32 API 调用的美好时光吗? 那么...它们在 Visual Basic .NET 中的工作方式与在 VB6 中一样好。 将以下声明放入可由你的窗体加载事件访问的范围内
Private Declare Function GetWindow Lib "user32" Alias "GetWindow" _
(ByVal hwnd As IntPtr, ByVal wCmd As Integer) As IntPtr
Private Const GW_CHILD As Integer = 5
Dim SysHdr32Handle As IntPtr
并在您的窗体加载事件中,添加以下内容
SysHdr32Handle = GetWindow(ListView1.Handle, GW_CHILD)
上面的代码假定您已经创建了一个窗体窗口并在其上绘制了一个 ListView
控件。 由于 ListView
控件只有一个子窗口(SysHeader32
窗口),我们可以放心,因为这个调用将获得其 Windows 句柄。
第二步
现在我们需要一种方法来拦截发送到子窗口的消息。 幸运的是,.NET Framework 类库包含一个专门为此目的设计的类 - NativeWindow
类。 该类的 MSDN 帮助描述如下:
提供对窗口句柄和窗口过程的低级封装。
要使用该类,我们需要创建一个继承 NativeWindow
类的新类,以便我们可以覆盖 WndProc
消息处理函数以执行我们想要的操作。 将以下类定义添加到您的窗体代码
Private Class ListViewHeader
Inherits System.Windows.Forms.NativeWindow
Private ptrHWnd As IntPtr
Protected Overrides Sub WndProc(ByRef m As _
System.Windows.Forms.Message)
' We'll add this in step 3
MyBase.WndProc(m)
End Sub
Protected Overrides Sub Finalize()
Me.ReleaseHandle()
MyBase.Finalize()
End Sub
Public Sub New(ByVal ControlHandle As IntPtr)
ptrHWnd = ControlHandle
Me.AssignHandle(ptrHWnd)
End Sub
End Class
当然,我们需要一个全局的窗体变量声明来使用,并且我们需要初始化我们的类实例。 将以下声明放入您的窗体类声明中
Private ListViewHeader1 As ListViewHeader
然后将以下代码行添加到您的窗体加载事件中
SysHdr32Handle = _
GetWindow(ListView1.Handle, GW_CHILD) ' <-- added this in step 1
ListViewHeader1 = New ListViewHeader(SysHdr32Handle)
步骤 3
现在我们有了一种接收消息的方法,我们可以添加代码来处理它们。 在这个简单的例子中,我们将拦截 WM_SETCURSOR
和 WM_LBUTTONDOWN
消息并将它们丢弃,以防止用户看到调整列大小的光标以及能够调整列的大小。 如下修改我们的 ListViewHeader
类的 WndProc
方法
Protected Overrides Sub WndProc(ByRef m _
As System.Windows.Forms.Message)
Select Case m.Msg
Case Is = &H20 ' WM_SETCURSOR
m.Msg = 0
Case Is = &H201 ' WM_LBUTTONDOWN
m.Msg = 0
End Select
MyBase.WndProc(m)
End Sub
结论
上面的示例提供了一种获取这些 SysHeader32
消息句柄的基本方法。 因此,该示例相当粗糙。 例如,如果您想禁用两个 ListView
控件的列调整大小,该怎么办? 按照上面编写的方式,您将不得不创建两个不同的类。 一个健壮的实现将向类定义添加事件并在 WndProc
方法中收到 Windows 消息时触发它们,从而允许类实例的创建者处理它们。 但是,目前,这将帮助你入门。