none
Measure width of a string RRS feed

  • Question

  • I realize this topic has been posted previously, however i am having some serious issues trying to implement it.

     

    i  have a checked list box that i want to essentially format into columns.  I use graphics.measurestring("this is a string to measure", font).width

     

    and i come up with a width.  However, these numbers don't really seem to work out for me.  According to this method, 1 space (" ") is only 4 pixels wide.  1 uppercase H ("H") is about 14 or so.  Graphically on my screen they are the exact same width, so if i'm trying to add spaces to get to a certain length, you can see where i would have some problems.

     

    I really need to be able to accurately measure the width of a string containing spaces in order to make my program more readable. The units don't matter, as long as they are consistent.

     

    Thanks for the help

    -Mark P.

    Tuesday, June 10, 2008 4:06 PM

Answers

  • Rather then formatting a ListBox into columns why don't you use a ListView? Or a DataGridView. These controls were designed to be into columns. Why use the wrong control for that task?

    Tuesday, June 10, 2008 4:10 PM
  • The spaces are accurately measured, but not when the string is spaces only, so you can get the true space width like this:

     

    Code Snippet

    Dim g As Graphics = Me.CreateGraphics

    Dim SpaceWidth As Double = g.MeasureString("X X").Width - g.MeasureString("XX").Width

     

     

     

    Tuesday, June 10, 2008 6:39 PM
  • Different strokes for different folks:

    Code Snippet

    Console.WriteLine(TextRenderer.MeasureText(" ", Font).Width.ToString)

    Console.WriteLine(TextRenderer.MeasureText("           ", Font).Width.ToString)

     

    Tuesday, June 10, 2008 8:52 PM

All replies

  • Rather then formatting a ListBox into columns why don't you use a ListView? Or a DataGridView. These controls were designed to be into columns. Why use the wrong control for that task?

    Tuesday, June 10, 2008 4:10 PM
  • The program creates a grade sheet or report for survey staking done in the field. I felt the checked list box was the best choice because the user is telling the program what data they want to export from the form.  Each item in the column represents 1 point staked, however, that 1 point has about 4 pieces of data associated with it that make it more easily identified to the end user.

     

    For example, the crew staked point #4501, which had a design elevation of 459.52 feet, they staked it at 458.23, which means the contractor needs to bring the finish grade up 1.29 feet (or a Fill of 1.29'), and the staked point was stored as 20001.

     

    All of this is really one item, which the user will export to excel.  There it will be one row with 10 columns of associated data, but here i just need to clue the user in to whether or not they want to export this particular point or not.

     

    Hope that wasn't too muddled.  Smile

    Tuesday, June 10, 2008 4:28 PM
  • The method you are referring to returns a struct SizeF. In that struct there is a Method that returns a ToSize() method. I am not sure if this is even the method you need. Can we see a use case of your code?

    Tuesday, June 10, 2008 4:53 PM
  • It really seems like you should be using a listview which has checkboxes, items, subitems and columns.  The listview is much more flexible than a listbox and closer to a spreadsheet layout. If you feel you must use a listbox, use a stringformat with tabs.
    Tuesday, June 10, 2008 5:34 PM
  • The spaces are accurately measured, but not when the string is spaces only, so you can get the true space width like this:

     

    Code Snippet

    Dim g As Graphics = Me.CreateGraphics

    Dim SpaceWidth As Double = g.MeasureString("X X").Width - g.MeasureString("XX").Width

     

     

     

    Tuesday, June 10, 2008 6:39 PM
  • The method is measuring the string accurately. Like you said, spaces takes much less space then letters (maybe except the "i" letter). And all depends on the font, some fonts the space is bigger than others, some letters are bigger than others. You have to take in mind that the Font AND, Font size and its modifiers (italic, bold, etc) are taken into account when measuring a string. You can verify this difference by checking the sample below (Arial 10):

    Space only string (10 spaces): "          "
    Letter 'H' only (10 characters):  "HHHHHHHHHH"

    Can you picture the difference? Now for another font in different size (Tahoma 12):

    Space only string (10 spaces): "          "
    Letter 'H' only (10 characters):  "HHHHHHHHHH"

    See how the difference changes? I the difference in size of space and the letter H is smaller. So if you are not getting the results you expect, make sure the font you are masuring matches the font you are using in the listbox, also the "text" must match with the text you are using in the control.

    I hope this helps you solve the problem.
    Regards,
    Fábio

    Tuesday, June 10, 2008 7:38 PM
  • Fábio, the poster is correct that the function is buggy when measuring space-only strings.  It returns the same value for the string

    " "

    as it does for the string

    "                                           "

     

    Furthermore, when using a fixed-type font, every character returns the same value.  A "." returns the same value as "W" but the space returns a different, incorrect value.

    Tuesday, June 10, 2008 7:59 PM
  • Different strokes for different folks:

    Code Snippet

    Console.WriteLine(TextRenderer.MeasureText(" ", Font).Width.ToString)

    Console.WriteLine(TextRenderer.MeasureText("           ", Font).Width.ToString)

     

    Tuesday, June 10, 2008 8:52 PM
  • I guess I missed the problem. Anyways it is easy to do a workaround in this case.
    In the case of fixed-width font (I think that is what you meant) like True Type fonts, I see no problem with the measure of the string, I tested and strings mixed with the ".", "W" and " ", return the same width, unless the string is composed of a single space (which we can do an easy workaround too).

    But for the rest of the cases it measures it correctly. Maybe if he needs a workaround I can supply a helper method for him later.

    Regards,
    Fábio
    Tuesday, June 10, 2008 8:52 PM
  • I guess John solved the problem
    Tuesday, June 10, 2008 8:55 PM
  • JohnWein is a genuis

     

    Tuesday, June 10, 2008 9:01 PM
  •  

    To answer some of your questions, yes i am measuring the same font in the header and the checked list box.  Arial 10pt.

    I tried measuring "X X" and then subtracting "XX" but i come up with 3, and when i measure a space i get 4.

     

    my code in a nutshell:

     

    this measures the header_lbl which is what i'm trying to use to establish the columns:

     

    Code Snippet

        Private Sub GetColumnSpacing()

            Dim gr As System.Drawing.Graphics = Me.CutSelection_clb.CreateGraphics
            Dim fnt As System.Drawing.Font = Me.header_lbl.Font

            'get rid of the spaces in the front and end of the header
            Dim lblText As String = Trim(Me.header_lbl.Text)
           
            storePntSize = InStr(lblText, "Store", CompareMethod.Text)
            cfSize = InStr(lblText, "Cut/Fill", CompareMethod.Text)
            gradeSize = InStr(lblText, "Grade", CompareMethod.Text)

            storePntSize = gr.MeasureString(Microsoft.VisualBasic.Left(lblText, storePntSize) + "|", fnt).Width
            cfSize = gr.MeasureString(Microsoft.VisualBasic.Left(lblText, cfSize) + "|", fnt).Width
            gradeSize = gr.MeasureString(Microsoft.VisualBasic.Left(lblText, gradeSize) + "|", fnt).Width

            gr.Dispose()

        End Sub

     

    Code Snippet

    Private Sub PopulateCutSheetListBox()

            GetColumnSpacing()
            Dim temp As String = ""

            For Each d() As String In csList

                If Not d(0) Is Nothing And InStr(d(0), "PP", CompareMethod.Text) = 0 Then 'there is a valid point nubmer
                    'sometimes the point number will have a negative double after it i.e.:  4501 -2.512
                    'i don't yet know what this is, but i don't want it displayed
                    If InStr(d(0), " ", CompareMethod.Text) > 0 Then
                        temp += Microsoft.VisualBasic.Left(d(0), InStr(d(0), " ", CompareMethod.Text))
                    Else
                        temp += d(0)
                    End If
                ElseIf Not d(1) Is Nothing Then 'there is a station and probably an offset
                    temp += d(1) + " " + d(2)
                End If

                'add the correct number of spaces to have the data format into columns properly
                temp = CorrectSpacing(gradeSize, temp)

                'add the design grade
                If Not d(3) Is Nothing Then temp += d(3)

                'add the correct number of spaces to have the data format into columns properly
                temp = CorrectSpacing(cfSize, temp)

                'add the Cut/Fill
                If Not d(13) Is Nothing Then
                    temp += "C-" + d(13)
                ElseIf Not d(14) Is Nothing Then
                    temp += "F-" + d(14)
                End If

                'add the correct number of spaces to have the data format into columns properly
                temp = CorrectSpacing(storePntSize, temp)

                'add the store point number
                If Not d(6) Is Nothing Then
                    temp += d(6)
                End If

                CutSelection_clb.Items.Add(temp, True)
                temp = ""
            Next
        End Sub

     

     

    Code Snippet

        Private Function CorrectSpacing(ByVal correctSizing As Integer, ByVal stringToCorrect As String) As String

            Using gr As System.Drawing.Graphics = Me.CutSelection_clb.CreateGraphics
                Dim fnt As System.Drawing.Font = Me.CutSelection_clb.Font

                Dim spaceSize As Integer = gr.MeasureString(" ", fnt).Width

                Dim currentSize As Integer = gr.MeasureString(stringToCorrect, fnt).Width

                If currentSize > correctSizing Then
                    Do While currentSize > correctSizing
                        stringToCorrect = Microsoft.VisualBasic.Left(stringToCorrect, Len(stringToCorrect) - 5)
                        currentSize = gr.MeasureString(stringToCorrect, fnt).Width
                    Loop
                End If

                Dim cnt As Integer = correctSizing - currentSize
                cnt = cnt / spaceSize

                For i As Integer = 0 To cnt
                    stringToCorrect += " "
                Next

            End Using

            Return stringToCorrect

        End Function

    End Class

     

     

     

    basically this assumes you have the header_lbl which is a label with the following text:

    "Point # / Station - Offset                        Grade               Cut/Fill                         Store Point #"

     

    it also assumes that you have a list of arrays named csList

    which is populated with a 18 index array of strings d().  I only use 4 items in the array which are of varying length.

    Tuesday, June 10, 2008 9:11 PM
  • I was able to get it working using the "X X" - "XX" method, but nevermind that -- see John Wein's reply above.  He reminds us that .NET 2.0 gave us TextRenderer.

    Tuesday, June 10, 2008 9:15 PM
  •  JohnGrove wrote:

    Rather then formatting a ListBox into columns why don't you use a ListView? Or a DataGridView. These controls were designed to be into columns. Why use the wrong control for that task?

    All johns are genii.  Your original response is the correct answer.

    Tuesday, June 10, 2008 9:41 PM
  • So, using textrenderer.measuretext(), which i've messed around with a bit..... see i DO know how to use the search function

     

    i come up with the following:

     

    "X X" = 32  (this is all in Arial, 10pt)

    "XX" = 28

    " " = 12

    now i'm no mathmatician.... but pretty sure 32-28 = 4.  So, i had kind of discounted this method because it made all my strings really, really short.  For every space i needed i was assuming that a space was 12 units, when the math tells me it should be 4.

     

    I think this will work out for me, i just need to compensate for any spaces that may already be part of the string.  Each of those spaces seems to throw off my calculations by 1 space???

    I am going to investigate this further, however, i need to get at least a little bit of work done today!

    Tuesday, June 10, 2008 9:42 PM
  • yes, i may switch over to a list view control..... however, this is quickly becoming a personal vendetta with .NET 

     

    Tuesday, June 10, 2008 9:49 PM