dcsimg
Results 1 to 11 of 11

Thread: Custom listview header redraw problem

  1. #1

    Thread Starter
    Member
    Join Date
    Nov 2016
    Location
    UK
    Posts
    42

    Custom listview header redraw problem

    I've been trying to subclass a listview control that allows a user to set a color property for the header background and have it automatically resize the columns to display all the header text when empty but I keep ending up with numerous redraw problems. I either end up with a gap at the end of the control or the first two columns are messed up. I've tried invalidating it in lots of different places or forcing it to redraw but I've only had small amounts of success.

    In certain circumstances, I can get it to display correctly if the total length of all the header text is greater than the length of the control and other times I've got it working if the total length of all the header text is less than the length of the control but never both.

    Name:  3.jpg
Views: 157
Size:  15.1 KB

    A few times I thought id cracked it only to find it was constantly redrawing in an infinite loop. I've added a custom event so I can check when it's stuck in a loop.

    Code:
    Imports System.Drawing.Drawing2D
    
    
    Public Class LVA
    
        Inherits ListView
    
        Private _HeaderBackColor As Color
        Private _HeaderFont As Font
    
        Public Sub New()
            Me.OwnerDraw = True
            Me.View = View.Details
            Me.FullRowSelect = True
    
            SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
            _HeaderFont = New Font("Segoe UI", 9.75, FontStyle.Regular)
            _HeaderBackColor = Color.Gray
        End Sub
    
        ' Draws column headers.
        Private Sub LVA_DrawColumnHeader(ByVal sender As Object, ByVal e As DrawListViewColumnHeaderEventArgs) Handles Me.DrawColumnHeader
    
            infoout("DrawColumnHeader")
    
            Dim sf As New StringFormat()
            Try
    
                ' Store the column text alignment, letting it default to Left if it has not been set to Center or Right.
                Select Case e.Header.TextAlign
                    Case HorizontalAlignment.Center : sf.Alignment = StringAlignment.Center
                    Case HorizontalAlignment.Right : sf.Alignment = StringAlignment.Far
                End Select
    
                sf.LineAlignment = StringAlignment.Center
    
                ' Draw the standard header background.
                e.DrawBackground()
    
                ' Draw the header text.
                e.Graphics.FillRectangle(New SolidBrush(_HeaderBackColor), e.Bounds)
                e.Graphics.DrawString(e.Header.Text, _HeaderFont, New SolidBrush(Color.Gainsboro), e.Bounds, sf)
    
                'Add a spacer
                Dim newcolor As Color = Color.FromArgb(_HeaderBackColor.ToArgb() Xor &HFFFFFF)
                If e.ColumnIndex < Columns.Count - 1 Then
                    e.Graphics.DrawLine(New Pen(Color.Black), e.Bounds.X, e.Bounds.Y + 2, e.Bounds.X, e.Bounds.Height - 2)
                End If
    
            Finally
                sf.Dispose()
            End Try
    
        End Sub
    
    
        Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
            MyBase.OnDrawColumnHeader(e)
            'trying to force a redraw here
           If Columns(e.ColumnIndex).Width <> GetTextWidth(e.Header.Text, _HeaderFont) + 5 Then
                e.Header.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize)
            End If
        End Sub
    
        Private Function GetTextWidth(txt As String, fnt As Font) As integer
            Dim sI As SizeF
    
            Using g As Graphics = Graphics.FromHwnd(IntPtr.Zero)
                sI = g.MeasureString(txt, fnt)
            End Using
    
            Return CInt(sI.Width)
        End Function
    
        Public Event NewInfo(ByVal e As eInfo)
    
        Private Sub InfoOut(str As String)
            RaiseEvent NewInfo(New eInfo(str))
        End Sub
    
    End Class
    
    
    Public Class eInfo
    
        Inherits EventArgs
    
        Private _SomeInfo As String
        Public Property SomeInfo() As String
            Get
                Return _SomeInfo
            End Get
            Set(ByVal value As String)
                _SomeInfo = value
            End Set
        End Property
    
        Public Sub New( NewInfo As String)
            MyBase.New
            _SomeInfo = NewInfo
        End Sub
    
    End Class
    I've also tried adding several types of Auto resize methods with only partial success.

    Code:
    ''' <summary>
        ''' Automatically resizes the columns to fit the column content or the column header text depending on which is the widest
        ''' </summary>
        Public Sub Auto_Column_Resize()
            Dim HeaderTextLength As Integer
            Dim cHeader As New ColumnHeader
            Dim LongestText As String
    
            For i = 0 To Columns.Count - 1
                    cHeader = Columns(i)
                    HeaderTextLength = cHeader.Text.Length
                    LongestText = cHeader.Text
                For inc = 0 To Items.Count - 1
    
                    If Items(inc).SubItems(i).Text.Length > HeaderTextLength Then
                        HeaderTextLength = Items(inc).SubItems(i).Text.Length
                        LongestText = Items(inc).SubItems(i).Text
                    End If
                Next
    
                cHeader.Width = GetTextWidth(LongestText, Font) + 5
                Next
    
        End Sub
    Last edited by LucasCain; Aug 13th, 2019 at 10:26 PM.

  2. #2
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    2,037

    Re: Custom listview header redraw problem

    have you tried

    Code:
    .AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
    or
    Code:
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            For Each col As ColumnHeader In ListView1.Columns
                col.Width = -2
            Next col
        End Sub
    hth
    Last edited by ChrisE; Aug 14th, 2019 at 02:27 AM.
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  3. #3

    Thread Starter
    Member
    Join Date
    Nov 2016
    Location
    UK
    Posts
    42

    Re: Custom listview header redraw problem

    Yes, that was some of the first stuff I tried.

    Code:
    .AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
    will only work if there are any items in the listview and what I'm after is to have it display the headers correctly when the listview has no items like when the listview is first displayed.

    Ive also tried the second bit of code. I put it into the event as this gets called after the sub DrawColumnHeader has been run:
    Code:
        Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
            MyBase.OnDrawColumnHeader(e)
            'trying to force a redraw here
    
            If e.ColumnIndex = Columns.Count - 1 Then
                For Each col As ColumnHeader In Columns
                    col.Width = -2
                Next col
            End If
    
        End Sub
    If the total width of the header text is less than the length of the control I get:
    Name:  a.jpg
Views: 44
Size:  14.1 KB

    and if its greater than the length of the control I get:
    Name:  c.jpg
Views: 44
Size:  16.5 KB

  4. #4
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    2,037

    Re: Custom listview header redraw problem

    try this

    Code:
    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
          
            ListView1.BeginUpdate()
       
            Dim width As Integer = 1
    
            For Each col As ColumnHeader In ListView1.Columns
                width = col.Width
    
                ' column items greatest width
                col.Width = -1
                If width > col.Width Then
                    col.Width = width
                End If
    
                ' column header width
                col.Width = -2
                If width > col.Width Then
                    col.Width = width
                End If
            Next col
    
            ListView1.EndUpdate()
    
        End Sub
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  5. #5
    Addicted Member Goggy's Avatar
    Join Date
    Oct 2017
    Posts
    158

    Re: Custom listview header redraw problem

    Code:
    Public Class ListViewEx
        Inherits System.Windows.Forms.ListView
    
        Public Sub New()
            Me.OwnerDraw = True
    
            Me.View = View.Details
    
        End Sub
        Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
    
            Dim Text As String = $"Column nr:{e.ColumnIndex.ToString}"
    
            Me.Columns(e.ColumnIndex).Width = e.Graphics.MeasureString(Text.Replace(" ", "_"), Me.Font).Width
    
    
            With e.Graphics
    
                ControlPaint.DrawBorder3D(e.Graphics, e.Bounds, Border3DStyle.Sunken, Border3DSide.All)
    
                Using Br As New SolidBrush(Color.Black.ToOpaque(128))
                    .FillRectangle(Br, e.Bounds)
                End Using
    
                .DrawString(Text, Me.Font, New SolidBrush(Color.Black), e.Bounds, New StringFormat With {.Alignment = StringAlignment.Center, .LineAlignment = StringAlignment.Center})
            End With
    
    
            MyBase.OnDrawColumnHeader(e)
        End Sub
    
    End Class
    
    Public Module ColorExtensions
        <Runtime.CompilerServices.Extension()>
        Public Function ToOpaque(C As Global.System.Drawing.Color, Opacity As Double) As Color
            Return Color.FromArgb(CInt(IIf(C.Equals(Color.Transparent), 0, Opacity)), C)
        End Function
    End Module
    Name:  ColumnHeaders.jpg
Views: 42
Size:  9.8 KB
    Utterly useless, but always willing to help

    As a finishing touch god created the dutch

  6. #6

    Thread Starter
    Member
    Join Date
    Nov 2016
    Location
    UK
    Posts
    42

    Re: Custom listview header redraw problem

    Quote Originally Posted by ChrisE View Post
    try this

    Code:
    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
          
            ListView1.BeginUpdate()
       
            Dim width As Integer = 1
    
            For Each col As ColumnHeader In ListView1.Columns
                width = col.Width
    
                ' column items greatest width
                col.Width = -1
                If width > col.Width Then
                    col.Width = width
                End If
    
                ' column header width
                col.Width = -2
                If width > col.Width Then
                    col.Width = width
                End If
            Next col
    
            ListView1.EndUpdate()
    
        End Sub
    Hi, thanks for the reply. There's a couple of problems with that bit of code, first it works off a button press where i want this to work automatically. Also where you have Width=col.width that just resizes the whole control. I changed the code to fit in the OnDrawColumnHeader event like this:

    Code:
     Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
            MyBase.OnDrawColumnHeader(e)
            'trying to force a redraw here
    
            BeginUpdate()
    
            If e.ColumnIndex = Columns.Count - 1 Then
                For Each col As ColumnHeader In Columns
                    Dim nWidth As Integer = col.Width
    
                    ' column items greatest width
                    col.Width = -1
                    If nWidth > col.Width Then
                        col.Width = nWidth
                    End If
    
                    ' column header width
                    col.Width = -2
                    If nWidth > col.Width Then
                        col.Width = Width
                    End If
                Next col
            End If
    
            EndUpdate()
        End Sub
    but it sends the control into an infinite loop.

  7. #7

    Thread Starter
    Member
    Join Date
    Nov 2016
    Location
    UK
    Posts
    42

    Re: Custom listview header redraw problem

    Hi, thanks for the reply. That bit of code leaves a white gap at the end of the headers if the total width of the headers is less than the width of the control which I've been trying to avoid because the main reason I've been making this control is so that it fits in with darker themes. Also if the total width of the headers is greater than the width of the control it chops off any headers to the right.

    Name:  d.jpg
Views: 42
Size:  15.0 KB

  8. #8
    Addicted Member Goggy's Avatar
    Join Date
    Oct 2017
    Posts
    158

    Re: Custom listview header redraw problem

    Second try:

    Code:
    Imports System.Linq
    
    Public Class ListViewEx
        Inherits System.Windows.Forms.ListView
    
        Public Sub New()
            Me.OwnerDraw = True
    
            Me.View = View.Details
    
        End Sub
        Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
    
            Dim Text As String = $"Column nr:{e.ColumnIndex.ToString}"
            Dim LeftOver As Double = Me.Width
    
            If e.ColumnIndex = Me.Columns.Count - 1 Then
                For index = 1 To Me.Columns.Count - 2
                    LeftOver -= Me.Columns(index).Width
                Next
                Me.Columns(e.ColumnIndex).Width = LeftOver
            Else
                Me.Columns(e.ColumnIndex).Width = e.Graphics.MeasureString(Text.Replace(" ", "_"), Me.Font).Width
            End If
    
            With e.Graphics
    
                ControlPaint.DrawBorder3D(e.Graphics, e.Bounds, Border3DStyle.Sunken, Border3DSide.All)
    
                Using Br As New SolidBrush(Color.Black.ToOpaque(128))
                    .FillRectangle(Br, e.Bounds)
                End Using
    
                .DrawString(Text, Me.Font, New SolidBrush(Color.Black), e.Bounds, New StringFormat With {.Alignment = StringAlignment.Center, .LineAlignment = StringAlignment.Center})
            End With
    
    
            MyBase.OnDrawColumnHeader(e)
        End Sub
    
    
    End Class
    
    Public Module ColorExtensions
        <Runtime.CompilerServices.Extension()>
        Public Function ToOpaque(C As Global.System.Drawing.Color, Opacity As Double) As Color
            Return Color.FromArgb(CInt(IIf(C.Equals(Color.Transparent), 0, Opacity)), C)
        End Function
    End Module
    Name:  ColumnHeaders.jpg
Views: 41
Size:  9.9 KB
    Utterly useless, but always willing to help

    As a finishing touch god created the dutch

  9. #9
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    2,037

    Re: Custom listview header redraw problem

    goggy is on the right track

    I changed hie idea... ....MeasureString
    to ..TextRenderer.MeasureText...

    Code:
     Private Function GetTextWidth(ByVal LiSub As ListViewItem.ListViewSubItem) As Integer
    
             Dim w As Integer = System.Windows.Forms.TextRenderer.MeasureText( _
                                                  LiSub.Text, LiSub.Font).Width
             Return w + 5
          End Function
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  10. #10

    Thread Starter
    Member
    Join Date
    Nov 2016
    Location
    UK
    Posts
    42

    Re: Custom listview header redraw problem

    have you tried filling that up with any content?

  11. #11

    Thread Starter
    Member
    Join Date
    Nov 2016
    Location
    UK
    Posts
    42

    Re: Custom listview header redraw problem

    Also, I just noticed, it's stuck in an infinite loop constantly redrawing.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width