VB.NET MeasureString

Thomasian
Thomasian used Ask the Experts™
on
How can I get a string's size in pixels?

I need to set the size of a picturebox such that it will exactly display 5 rows with 32 chars/row.

I tried to use MeasureString method to get the character size of the font. I will be using a monospaced font so I just set the width to 32*character width and the height to 5*character height. But the picturebox can display about 5.5rows and 49 chars/row.
Private myFont As Font
    Private FontSizes As Drawing.SizeF
    Private CharsPerRow As Integer = 32
    Private Rows As Integer = 5

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        myFont = New Font("Courier New", 18, FontStyle.Bold)
        FontSizes = e.Graphics.MeasureString("X", myFont)
        PB.Width = Convert.ToInt32(FontSizes.Width * CharsPerRow)
        PB.Height = Convert.ToInt32(FontSizes.Height * Rows)
    End Sub

    Private Sub PB_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PB.Paint
        Dim strTest As String = ""
        For idx As Integer = 1 To 9
            strTest &= "12345678911234567892123456789312" & vbNewLine
        Next
        strTest &= "12345678911234567892123456789312"
        e.Graphics.DrawString(strTest, myFont, Brushes.Black, 0, 0)
    End Sub

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
Hello Thomasian,


You need to take into account char spacing and line spacing.

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        myFont = New Font("Courier New", 18, FontStyle.Bold)

        Dim sToMeasure As String = ""
        Dim i As Integer, j As Integer

        For i = 1 To Rows
            For j = 1 To CharsPerRow
                sToMeasure += "X"
            Next j
            If i <> Rows Then sToMeasure += Environment.NewLine
        Next i

        FontSizes = e.Graphics.MeasureString(sToMeasure, myFont)
        PB.Width = Convert.ToInt32(FontSizes.Width)
        PB.Height = Convert.ToInt32(FontSizes.Height)
    End Sub


Regards,

Rimvis

Author

Commented:
Thanks. That worked.

But is there a way to get the character and line spacing so that I can just compute the size using the size of a character?

Commented:
There is no direct method, but you can calculate it:

        Dim fCharSpacing As Double, fLineSpacing As Double
        fCharSpacing = e.Graphics.MeasureString("XX", myFont).Width _
                        - (e.Graphics.MeasureString("X", myFont).Width * 2)
        fLineSpacing = e.Graphics.MeasureString("X" + Environment.NewLine + "X", myFont).Height _
                        - (e.Graphics.MeasureString("X", myFont).Height * 2)
        FontSizes = e.Graphics.MeasureString("X", myFont)

        PB.Width = Convert.ToInt32(FontSizes.Width * CharsPerRow + fCharSpacing * (CharsPerRow - 1))
        PB.Height = Convert.ToInt32(FontSizes.Height * Rows + fLineSpacing * (Rows - 1))
Angular Fundamentals

Learn the fundamentals of Angular 2, a JavaScript framework for developing dynamic single page applications.

Author

Commented:
I didn't notice it earlier because I had BorderStyle set to FixedSingle, but part of the 33rd chars is still displayed for both codes. Maybe we are still missing something?

Commented:
No problem at my side. See attached file. Are you sure you didn't made any modifications to code?


screenshot.png

Commented:
No border:

noborder.png

Author

Commented:
I modified the code to display more than 32 chars. But since the size of the picturebox can only display exactly 32 chars, the 33rd char should not be displayed.

I also though that this could be the possible cause:

  PB.Width = Convert.ToInt32(FontSizes.Width)

So I changed to such that the value will always be rounded down:

  PB.Width = Convert.ToInt32(Math.Floor(FontSizes.Width))


I also noticed that this only occurs when the charsperrow is even.
Private Sub PB_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PB.Paint
        Dim strTest As String = ""
        For idx As Integer = 1 To 9
            strTest &= "12345678911234567892123456789312345678941234567895123456789612345678971234567898" & vbNewLine
        Next
        strTest &= "12345678911234567892123456789312"
        e.Graphics.DrawString(strTest, myFont, Brushes.Black, 0, 0)
    End Sub

Open in new window

PictureBoxSize.jpg

Author

Commented:
*Ignore the last line. It also occurs when charsperrow is odd.

Btw, the screen shot I posted was when I changed the CharsPerRow is set to 61
Commented:
Yes, I could reproduce your problem with 61 chars.

Found a solution here:
Bypass Graphics.MeasureString limitations
http://www.codeproject.com/KB/GDI-plus/measurestring.aspx


        Dim x As Integer = MeasureDisplayStringWidth(e.Graphics, sToMeasure, myFont)
        PB.Width = x

    Public Shared Function MeasureDisplayStringWidth(ByVal graphics As Graphics, ByVal text As String, ByVal font As Font) As Integer
        Dim format As New System.Drawing.StringFormat()
        Dim rect As New System.Drawing.RectangleF(0, 0, 1000, 1000)
        Dim ranges As System.Drawing.CharacterRange() = {New System.Drawing.CharacterRange(0, text.Length)}
        Dim regions As System.Drawing.Region() = New System.Drawing.Region(0) {}

        format.SetMeasurableCharacterRanges(ranges)

        regions = graphics.MeasureCharacterRanges(text, font, rect, format)
        rect = regions(0).GetBounds(graphics)

        Return CInt((rect.Right + 1.0F))
    End Function


Author

Commented:
You even translated the code for me!

Thanks!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial