Bogdan Samchuk
Bogdan Samchuk

Reputation: 168

Text-rendering in handler of Paint has wrong size

The class inherits System.Windows.Forms.Label (best set of features for this control).

Size of control are set by AutoSize property of the base class (must be just enough), but the DrawString draws little wider or narrower, depends on the used font. With big sizes of font, height can be wrong too.

I presume that Ladel and Graphics use different modes of rendering, but can not understand this differences.

Or it is something wrong with the code?

Public Class LabelProgressBar
    Inherits Label

    Private dProgress As Double = 0.0
    Private nBackAlpha As Byte = 64
    Private stBarColor0 As Color = Color.Maroon
    Private stBarColor1 As Color = Color.ForestGreen

    Public Property BackAlpha As Byte
        Get
            Return nBackAlpha
        End Get
        Set(value As Byte)
            If value <> nBackAlpha Then
                nBackAlpha = value
                Invalidate()
            End If
        End Set
    End Property

    Public Property BarColor0 As Color
        Get
            Return stBarColor0
        End Get
        Set(value As Color)
            If value <> stBarColor0 Then
                stBarColor0 = value
                Invalidate()
            End If
        End Set
    End Property

    Public Property BarColor1 As Color
        Get
            Return stBarColor1
        End Get
        Set(value As Color)
            If value <> stBarColor1 Then
                stBarColor1 = value
                Invalidate()
            End If
        End Set
    End Property

    Public Property Progress As Double
        Get
            Return dProgress
        End Get
        Set(value As Double)
            If value <> dProgress Then
                Dim fOld = InnerProgress
                dProgress = value
                If InnerProgress <> fOld Then Invalidate()
            End If
        End Set
    End Property

    Private ReadOnly Property InnerProgress As Single
        Get
            If dProgress < 0.0 Then Return 0.0
            If dProgress > 1.0 Then Return 1.0
            Return CSng(Progress)
        End Get
    End Property

    Private Sub LabelProgressBar_PaddingChanged(sender As Object, e As EventArgs) Handles Me.PaddingChanged
        Invalidate()
    End Sub

    Private Sub LabelProgressBar_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
        If Width - Padding.Left - Padding.Right > 0 AndAlso Height - Padding.Top - Padding.Bottom > 0 Then
            e.Graphics.CompositingQuality = CompositingQuality.HighQuality
            e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
            e.Graphics.Clear(BackColor)
            PaintGradient(e.Graphics, e.ClipRectangle, 1.0, nBackAlpha)
            PaintGradient(e.Graphics, e.ClipRectangle, InnerProgress, 255)
            Dim stNonPadded = New RectangleF(e.ClipRectangle.Location, e.ClipRectangle.Size)
            stNonPadded.X = Padding.Left
            stNonPadded.Width -= Padding.Left + Padding.Right
            stNonPadded.Y = Padding.Top
            stNonPadded.Height -= Padding.Top + Padding.Bottom
            Using objBrush = New SolidBrush(ForeColor)
                Using objFormat = New StringFormat()
                    Select Case TextAlign
                        Case ContentAlignment.TopLeft, ContentAlignment.MiddleLeft, ContentAlignment.BottomLeft
                            objFormat.Alignment = StringAlignment.Near
                        Case ContentAlignment.TopCenter, ContentAlignment.MiddleCenter, ContentAlignment.BottomCenter
                            objFormat.Alignment = StringAlignment.Center
                        Case Else
                            objFormat.Alignment = StringAlignment.Far
                    End Select
                    objFormat.Trimming = If(AutoEllipsis, StringTrimming.EllipsisWord, StringTrimming.Character)
                    Select Case TextAlign
                        Case ContentAlignment.MiddleLeft, ContentAlignment.MiddleCenter, ContentAlignment.MiddleRight
                            Dim stDrawSize = e.Graphics.MeasureString(Text, Font, stNonPadded.Size, objFormat)
                            stNonPadded.Y += (stNonPadded.Height - stDrawSize.Height) / 2
                        Case ContentAlignment.BottomLeft, ContentAlignment.BottomCenter, ContentAlignment.BottomRight
                            Dim stDrawSize = e.Graphics.MeasureString(Text, Font, stNonPadded.Size, objFormat)
                            stNonPadded.Y += stNonPadded.Height - stDrawSize.Height
                    End Select
                    e.Graphics.DrawString(Text, Font, objBrush, stNonPadded, objFormat)
                End Using
            End Using
        End If
    End Sub

    Private Sub LabelProgressBar_TextAlignChanged(sender As Object, e As EventArgs) Handles Me.TextAlignChanged
        Invalidate()
    End Sub

    Private Sub PaintGradient(surface As Graphics, bounds As Rectangle, progress As Single, alpha As Byte)
        Dim stColor0 = Color.FromArgb(alpha, stBarColor0)
        Dim stColor1 = Color.FromArgb(alpha, stBarColor1)
        Using objBrush = New LinearGradientBrush(bounds, stColor0, stColor1, LinearGradientMode.Horizontal)
            surface.FillRectangle(objBrush, New RectangleF(bounds.Left, bounds.Top, bounds.Width * progress, bounds.Height))
        End Using
    End Sub

End Class

Upvotes: 0

Views: 57

Answers (1)

PavlinII
PavlinII

Reputation: 1083

Why don't you let the label to do the drawstring job for you?

Protected Overrides Sub OnPaintBackground(e As PaintEventArgs)
    MyBase.OnPaintBackground(e)
    If Width - Padding.Left - Padding.Right > 0 AndAlso Height - Padding.Top - Padding.Bottom > 0 Then
        e.Graphics.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
        e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
        e.Graphics.Clear(BackColor)
        PaintGradient(e.Graphics, e.ClipRectangle, 1.0, nBackAlpha)
        PaintGradient(e.Graphics, e.ClipRectangle, InnerProgress, 255)
    End If
End Sub

MeasureString/DrawString is little more complicated than you would expect. I can see the last character beeing stripped (not rendered) when DrawString with rectangle argument is used. You can avoid this by using this line

e.Graphics.DrawString(Text, Font, objBrush, stNonPadded.Location, objFormat)

but I'm not sure if that's your problem since your description was not very clear about it.

Also align according to ClipRectangle is not good idea since it can be half of your control when form is moving, hidden, partially hidden, moved out of the screen and so on.

Upvotes: 1

Related Questions