I found no better yet (*) than using the DrawColumnHeader event to capture X location of first item, column headers are redrawn when listview is scrolled horizontally also. This requires OwnerDraw set to True for the ListView, but you don't actually need to draw anything thanks to e.DrawDefault.
I didn't fancy covering up any item row, so when I tested this I just added a Panel above the Listview to hold the filter boxes. Using a Panel has the benefit of allowing some boxes to be positioned outside the visible area, so they will scroll horizontally in and out of view like the listview items. I set the height to 20 and same width as the Listview.
These are the events handled:
Private Sub ListView1_ColumnWidthChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnWidthChangedEventArgs) Handles ListView1.ColumnWidthChanged
AlignBoxes(True)
End Sub
Private Sub ListView1_DrawColumnHeader(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawListViewColumnHeaderEventArgs) Handles ListView1.DrawColumnHeader
e.DrawDefault = True
AlignBoxes()
End Sub
Private Sub ListView1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawListViewItemEventArgs) Handles ListView1.DrawItem
e.DrawDefault = True
End Sub
AlignBoxes method is used here to both add some textboxes if not already, and align them when columns width changes and when column headers are drawn. Using the currentOffset variable helps prevent a lot of unnecessary processing when header redraws for other reasons.
Private initialized As Boolean = False
Private currentOffset As Integer
Private Sub AlignBoxes(Optional ByVal force As Boolean = False)
If Not initialized Then
For i As Integer = 0 To Me.ListView1.Columns.Count - 1
Dim box As New TextBox
box.Name = "box" & i.ToString
Me.SearchPanel.Controls.Add(box)
Next
initialized = True
End If
Dim start As Integer = Me.ListView1.Items(0).Position.X
If force OrElse start <> Me.currentOffset Then
Me.currentOffset = start
For i As Integer = 0 To Me.ListView1.Columns.Count - 1
Dim header As ColumnHeader = Me.ListView1.Columns(i)
Dim box As Control = Me.SearchPanel.Controls("box" & i.ToString)
box.SetBounds(start, 0, header.Width, 20)
start += header.Width
Next
End If
End Sub
(*) There is also the windows message WM_HSCROLL that can be used, if you subclass the Listview or use a NativeWindow to listen, both both these methods means more work for no gain here.