Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
310 views
in Technique[技术] by (71.8m points)

c# - Automatically fit Columns to Listview for Vertical Scrollbar

I've inherited a Listview to perform some minor changes but I would like to improve the design in the usercontrol Class or anywhere in the Form Class 'cause I'm not happy with the default resizing mechanism of a Listview.

In the Form Class I resize the last column ("Download") like this:

ColumnDownload.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize)

The problem is that when the (default) scrollbar appears inside the listview the size of the last column is not automatically fixed/decreased so an horizontal and uneedded scrollbar automatically appears too.

Then someone could help me please to explain me how can be done this to auto-fix the size of the last column when the scrollbar appears? I'm very lost about that.

NOTE: I need to say that I'm not looking for alternative LV's such as ObjectListView.

NOTE 2: I don't show my usercontrol class 'cause I'm not performing any auto-resize improvement at the moment there, I don't know where to start.

This is my Listview as normal:

enter image description here

And this is the same listview filled with items:

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

when the (default) scrollbar appears inside the listview the size of the last column is not automatically fixed/decreased ... "default scrollbar" taken to mean the Veritical Scroll.

AutoResize is not AutoFit. It is for sizing columns based on the Header text extent or content length, not managing scrollbars and works very well. If it did automatically resize the last column, we would be angry when the last column is a small 'OK?' type column which was rendered unreadable when it was AutoFitted away.

PInvokes in a NativeMethods class; these eat scrollbars and get whether they are showing or not.

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

Steal xx pixels from the last column; Put the pixels back if the VScroll goes away.

Basically what happens is that ClientSizeChanged fires twice: 1) when the VScroll arrives then again a few micros later when the HScroll arrives as a result of the VScroll. So you have to process the event twice. This resizes the desired column in the first pass, and eats the HScroll in the second one. Even though the HScroll is no longer needed after Step 1, it shows up briefly if you dont remove it manually.

If an HScroll is needed due to the way you laid it out or from the user resizing columns, it will resize the last column anyway, but not eat the HScroll. The extra logic to detect when the first step is not needed is missing (you cant see that it does it, normally).

Caveats: I have no idea how or if this will work with this 3rd party theme. I dont think that the themes can modify internal scrollbars like these.

Also, this is intended for use in a subclassed LV, which I know you have already done. For others, with some reworking of references, this should also work on a form to 'push' the changes using the related events (e.g. ClientSizeChanged) even if it leaves a lot of ugly code in the form.

This also isnt wrapped in something like an IF AutoFit property test.

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

Put this somewhere like ISupportInitialize.EndInit:

orgClient = Me.ClientRectangle

Sub New is not a good place for orgClient - the control hasnt been created yet. OnHandleCreated will do, but without ISupportInitialize, I would invoke a new Sub on the control to set it from Form_Load instead. This can actually be handy to have, if you want to 'restart' things after manually resizing columns then back again. For example:

' 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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...