自动将列装入Listview以获得垂直滚动条

时间:2014-07-12 18:30:47

标签: c# .net vb.net winforms listview

我继承了Listview以执行一些小的更改,但我想改进usercontrol类或Form Class中的任何地方的设计,因为我对默认的大小调整不满意Listview的机制。

在表单类中,我调整了最后一列(" Download"),如下所示:

ColumnDownload.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize)

问题是当(默认)滚动条出现在列表视图中时,最后一列的大小不会自动固定/减少,因此水平和未嵌入的滚动条也会自动显示。

然后有人可以帮我解释一下如何在滚动条出现时自动修复最后一列的大小?我很失落。

注意:我需要说我不是在寻找其他LV,例如 ObjectListView

注意2 :我没有显示我的用户控制课程,因为我现在没有执行任何自动调整大小的改进,我没有'知道从哪里开始。

这是我正常的Listview:

enter image description here

这与填充项目的列表视图相同:

enter image description here

2 个答案:

答案 0 :(得分:3)

when the (default) scrollbar appears inside the listview the size of the last column is not automatically fixed/decreased ..."默认滚动条"用来表示Veritical Scroll。

AutoResize不是AutoFit。它用于根据页眉文本范围或内容长度调整列的大小,而不是管理滚动条并且工作得很好。如果它确实自动调整了最后一列的大小,当最后一栏很小时,我们会生气吗?好的?'键入自动装配时无法读取的列。

PInvokes在NativeMethods班级;这些吃滚动条,并得到它们是否显示。

Public Const WS_VSCROLL As Integer = &H200000
Public Const WS_HSCROLL As Integer = &H100000
Friend Enum SBOrientation As Integer
    SB_HORZ = &H0
    SB_VERT = &H1
    SB_CTL = &H2
    SB_BOTH = &H3
End Enum

<DllImport("user32.dll")> _
Private Shared Function ShowScrollBar(ByVal hWnd As IntPtr,
                     ByVal wBar As Integer,
                     <MarshalAs(UnmanagedType.Bool)> ByVal bShow As Boolean
                     ) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowLong(ByVal hWnd As IntPtr, 
             ByVal nIndex As Integer) As Integer
End Function

Friend Shared Function IsHScrollVisible(ByVal ctl As Control) As Boolean
    Dim wndStyle As Integer = GetWindowLong(ctl.Handle, GWL_STYLE)
    Return ((wndStyle And WS_HSCROLL) <> 0)
End Function

Friend Shared Function IsVScrollVisible(ByVal ctl As Control) As Boolean
    Dim wndStyle As Integer = GetWindowLong(ctl.Handle, GWL_STYLE)
    Return ((wndStyle And WS_VSCROLL) <> 0)
End Function

Friend Shared Sub ShowHideScrollBar(ByVal ctl As Control, 
        ByVal sb As SBOrientation, ByVal bShow As Boolean)
     ShowScrollBar(ctl.Handle, sb, bShow)
End Sub

从最后一列中窃取xx像素;如果VScroll消失,则将像素放回原位。

基本上发生的事情是ClientSizeChanged发生两次:1)当VScroll到达时,然后在HScroll由于VScroll而到达时再次出现几个微观。所以你必须处理两次事件。这将在第一遍中调整所需列的大小,并在第二行中调用HScroll。即使在步骤1之后不再需要HScroll,如果您不手动删除它,它会短暂显示。

如果由于你的布局方式或用户调整列的大小而需要HScroll,它会调整最后一列的大小,但不会吃掉HScroll。缺少检测何时不需要第一步的额外逻辑(您通常无法看到它执行此操作)。

警告:我不知道这是否适用于第三方主题。我不认为主题可以修改这些内部滚动条。

另外,这个用于子类LV ,我知道你已经完成了。对于其他人,通过对参考文献进行一些修改,这也应该适用于“推送”的表格。使用相关事件(例如ClientSizeChanged)的更改,即使它在表单中留下了许多丑陋的代码。

这也不包含在IF AutoFit属性测试中。

Private orgClient As Rectangle      ' original client size for comparing
Private _VScrollWidth As Integer 

Private _resizedCol As Integer = -1
Private _VScroll As Boolean = False    ' persistent Scroll flags 
Private _HScroll As Boolean = False

' 3rd party???
_VScrollWidth = System.Windows.Forms.SystemInformation.VerticalScrollBarWidth

把它放在ISupportInitialize.EndInit

之类的地方
orgClient = Me.ClientRectangle

Sub New orgClient的好地方 - 尚未创建控件。 OnHandleCreated会这样做,但如果没有ISupportInitialize,我会在控件上调用一个新的Sub来代替Form_Load。如果你想重新启动,这实际上很方便。手动调整列后再重新生成的东西。例如:

' method on subclassed LV:   from form load: thisLV.ResetClientSize

Public Sub ResetClientSize          
   orgClient = Me.ClientRectangle   
End Sub

' a helper:
Private Function AllColumnsWidth() As Integer
    Dim w As Integer = 0
    For Each c As ColumnHeader In Columns
        w += c.Width
    Next
    Return w
End Function

' The Meat
Protected Overrides Sub OnClientSizeChanged(ByVal e As System.EventArgs)
    Dim VScrollVis As Boolean
    Dim HScrollVis As Boolean

    ' get who is Now Showing...local vars
    VScrollVis = NativeMethods.IsVScrollVisible(Me)
    HScrollVis = NativeMethods.IsHScrollVisible(Me)

     ' width change
    Dim delta As Integer = (orgClient.Width - ClientRectangle.Width)

    Dim TotalWidth As Integer = AllColumnsWidth()
    If (TotalWidth < ClientRectangle.Width - _VScrollWidth) And 
           (_resizedCol = -1) Then
        Exit Sub
    End If

    Me.SuspendLayout()

    ' If VScroll IS showing, but WASNT showing the last time thru here
    '  ... then we are here because VScroll just appeared.
    ' That being the case, trim the desired column
    If VScrollVis And _VScroll = False Then
        ' a smarter version finds the widest column and resizes THAT one
        _resizedCol = Columns.Count - 1

        ' we have to wait for the HScroll to show up
        ' to remove it
        Columns(_resizedCol).Width -= (delta + 1)

    End If

    ' HScroll just appeared
    If HScrollVis And (delta = _VScrollWidth) Then

        ' HScroll popped up, see if it is needed
        If AllColumnsWidth() <= orgClient.Width Then
            ' no, go away foul beast !
            NativeMethods.ShowHideScrollBar(Me,
                        NativeMethods.SBOrientation.SB_HORZ, False)

            _HScroll = False             ' hopefully

            ' allows us to set it back if the VScroll disappears
            orgClient = ClientRectangle
        Else
            ' ToDo: use this to detect when none of this is needed
            _HScroll = HScrollVis
        End If
    End If

    ' If VScroll ISNOT showing, but WAS showing the last time thru here
    '   ...then we are here because VScroll disappeared
    If VScrollVis = False And _VScroll = True Then
        ' put back the pixels

        If _resizedCol <> -1 Then
            Columns(_resizedCol).Width += (_VScrollWidth + 1)
            ' reset column tracker
            _resizedCol = -1
            ' reset to new compare size
            orgClient = ClientRectangle
        End If

    End If

    _VScroll = VScrollVis

    Me.ResumeLayout()

End Sub

答案 1 :(得分:0)

或多或少我已经完成了所以我发布了这个解决方案,代码仍有问题&#39;因为在某些我尝试过的情况下它没有按预期工作&# 34;播放&#34;显示和隐藏滚动条,它可以挂起列表视图,并确保代码有不必要的检查,可以改进,但现在我没有更多的时间来尝试修复相关的问题,如果有人可以帮助修复此代码然后改进它,那么多:

Dim SizeAlreadyDecreased As Boolean = False

Private Sub ElektroListView_Downloads_ClientSizeChanged(sender As Object, e As EventArgs) _
Handles ElektroListView_Downloads.ClientSizeChanged

    ' Retrieve all the column widths
    Dim AllColumnsWidth As Integer =
        (From col As ColumnHeader In CType(sender.Columns, ListView.ColumnHeaderCollection)
         Select col.Width).Sum

    ' Fix the last column width to fill the not-used blank space when resizing the Form.
    Me.ColumnDownload.Width =
        Me.ColumnDownload.Width + (sender.ClientSize.Width - AllColumnsWidth) - 2

    ' Fix the last column width to increase or decrease the size if a VertivalScrollbar appears.
    If GetScrollbars(sender) AndAlso Not SizeAlreadyDecreased Then
        SizeAlreadyDecreased = True
        ColumnDownload.Width -= SystemInformation.VerticalScrollBarWidth

    ElseIf GetScrollbars(sender) AndAlso SizeAlreadyDecreased Then
        SizeAlreadyDecreased = True

    ElseIf Not GetScrollbars(sender) AndAlso SizeAlreadyDecreased Then
        SizeAlreadyDecreased = False
        ColumnDownload.Width += SystemInformation.VerticalScrollBarWidth

    ElseIf Not GetScrollbars(sender) AndAlso Not SizeAlreadyDecreased Then
        SizeAlreadyDecreased = False

    End If

End Sub

Private Const WS_VSCROLL As Integer = &H200000I
Private Const WS_HSCROLL As Integer = &H100000I
Private Const GWL_STYLE As Integer = -16I

<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Shared Function GetWindowLong(
        ByVal hWnd As HandleRef,
        ByVal nIndex As Integer
) As Integer
End Function

Public Function GetScrollbars(ByVal ctrl As Control) As ScrollBars

    Dim style As Integer = GetWindowLong(New HandleRef(ctrl, ctrl.Handle), GWL_STYLE)

    Dim HScroll As Boolean = ((style And WS_HSCROLL) = WS_HSCROLL)
    Dim VScroll As Boolean = ((style And WS_VSCROLL) = WS_VSCROLL)

    If (HScroll AndAlso VScroll) Then
        Return ScrollBars.Both

    ElseIf HScroll Then
        Return ScrollBars.Horizontal

    ElseIf VScroll Then
        Return ScrollBars.Vertical

    Else
        Return ScrollBars.None

    End If

End Function