-
Aug 13th, 2019, 09:35 PM
#1
Thread Starter
Member
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.
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.
-
Aug 14th, 2019, 02:23 AM
#2
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.
-
Aug 14th, 2019, 08:04 AM
#3
Thread Starter
Member
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:
and if its greater than the length of the control I get:
-
Aug 14th, 2019, 08:35 AM
#4
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.
-
Aug 14th, 2019, 08:42 AM
#5
Addicted Member
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
Utterly useless, but always willing to help
As a finishing touch god created the dutch
-
Aug 14th, 2019, 08:50 AM
#6
Thread Starter
Member
Re: Custom listview header redraw problem
Originally Posted by ChrisE
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.
-
Aug 14th, 2019, 09:02 AM
#7
Thread Starter
Member
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.
-
Aug 14th, 2019, 09:09 AM
#8
Addicted Member
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
Utterly useless, but always willing to help
As a finishing touch god created the dutch
-
Aug 14th, 2019, 10:45 AM
#9
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.
-
Aug 22nd, 2019, 05:06 AM
#10
Thread Starter
Member
Re: Custom listview header redraw problem
have you tried filling that up with any content?
-
Aug 22nd, 2019, 05:44 AM
#11
Thread Starter
Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|