禁用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 消息时触发它们,从而允许类实例的创建者处理它们。 但是,目前,这将帮助你入门。

