none
Why is this code buggy? RRS feed

  • Question

  • Hello MSDN!

    I'm attempting to display the number of lines in a RTB on a column (colored picturebox) to the left. The effect will be the same as is seen in most IDE code editors (Visual Studio), as well as basic code editors (Notepad ++).

    Here's my code so far:

    Public Class Form1
    
        Dim total_lines As Integer
        Private Sub DrawRichTextBoxLineNumbers(ByRef g As Graphics)
    
            With RichTextBox1
                Dim font_height As Single
                font_height = .GetPositionFromCharIndex(.GetFirstCharIndexFromLine(2)).Y _
             - .GetPositionFromCharIndex(.GetFirstCharIndexFromLine(1)).Y
                If font_height = 0 Then Exit Sub
    
                Dim first_index As Integer
                Dim first_line As Integer
                Dim first_line_y As Integer
                first_index = .GetCharIndexFromPosition(New _
             Point(0, g.VisibleClipBounds.Y + font_height / 3))
                first_line = .GetLineFromCharIndex(first_index)
                first_line_y = .GetPositionFromCharIndex(first_index).Y
                total_lines = RichTextBox1.GetLineFromCharIndex(Int32.MaxValue) + 1
    
                Dim i As Integer = first_line
                Dim y As Single
                Do While y < g.VisibleClipBounds.Y + g.VisibleClipBounds.Height
                    y = first_line_y + 2 + font_height * (i - first_line - 1)
                    If total_lines >= i Then
                        g.DrawString((i).ToString, .Font, Brushes.White, PictureBox1.Width _
                   - g.MeasureString((i).ToString, .Font).Width, y)
                    Else
                        Exit Do
                    End If
                    i += 1
                Loop
            End With
        End Sub
    
        Private Sub r_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles RichTextBox1.Resize
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub r_VScroll(ByVal sender As Object, ByVal e As System.EventArgs) Handles RichTextBox1.VScroll
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub p_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
            DrawRichTextBoxLineNumbers(e.Graphics)
        End Sub
    
        Private Sub MyRichTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RichTextBox1.TextChanged
            If RichTextBox1.GetLineFromCharIndex(Int32.MaxValue) + 1 <> total_lines Then PictureBox1.Invalidate()
        End Sub
    End Class

    These are the bugs that I get:

    Could somebody possible talk me through why it is I'm getting these bugs/ point me in the right direction; I'm not understanding why it's not working.

    Thank you to anybody who can help.

    PS: The gray column on the left is the picturebox.
    Sunday, December 17, 2017 8:57 PM

Answers

  •  Perhaps this is what you are trying to do...  The PictureBox BorderStyle is set to BorderStyle.None.

    Public Class Form1
        Private Sub RichTextBox1_Resize(sender As Object, e As EventArgs) Handles RichTextBox1.Resize
            PictureBox1.Height = RichTextBox1.Height
        End Sub
    
        Private Sub RichTextBox1_VScroll(sender As Object, e As EventArgs) Handles RichTextBox1.VScroll
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub RichTextBox1_TextChanged(sender As Object, e As EventArgs) Handles RichTextBox1.TextChanged
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
            DrawLineNumbers(e.Graphics)
        End Sub
    
        Private Sub DrawLineNumbers(g As Graphics)
            Dim linenumbers As Integer = RichTextBox1.GetLineFromCharIndex(RichTextBox1.TextLength)
            For i As Integer = 0 To linenumbers
                Dim RtbBorderSize As Integer = (RichTextBox1.Height - RichTextBox1.ClientSize.Height) \ 2
                Dim y As Integer = RichTextBox1.GetPositionFromCharIndex(RichTextBox1.GetFirstCharIndexFromLine(i)).Y
                Dim r As New Rectangle(PictureBox1.ClientSize.Width - 100, y + RtbBorderSize, 100, RichTextBox1.Font.Height)
                TextRenderer.DrawText(g, i.ToString, RichTextBox1.Font, r, Color.White, TextFormatFlags.VerticalCenter Or TextFormatFlags.Right Or TextFormatFlags.SingleLine)
            Next
        End Sub
    End Class
    
     

     


    If you say it can`t be done then i`ll try it

    Monday, December 18, 2017 5:38 AM
  •  If you look in the DrawLineNumbers sub of my example,  you will see that the line numbers are draw by using the i value of the For Next loop which starts at 0.  You can just add 1 to the number as it is being drawn by changing the line below,  as shown.

    TextRenderer.DrawText(g, (i + 1).ToString, RichTextBox1.Font, r, Color.White, TextFormatFlags.VerticalCenter Or TextFormatFlags.Right Or TextFormatFlags.SingleLine)
     

     

     

     The reason for dividing the RtbBorderSize by two is because,  the RichTextBox has a Fixed3D border style (3 pixels wide) and the PictureBox has no border because it's border style is set to None,  no border.  In order to make the drawn line numbers line up with the lines in the RichTextBox,  you need to add just the top border size of the RichTextBox to the Y location of the line numbers when they are drawn.

     The part (below) of the line in question,  is taking the full height of the RichTextBox (including top and bottom borders) and subtracting the ClientSize height of the RichTextBox (no borders included),  from it.  That leaves you with the size of the Top and Bottom borders together (as one number).  That has to be divided by 2,  to get the size of just one border which is what you need to add to the Y location of the line numbers when drawing them.

    (RichTextBox1.Height - RichTextBox1.ClientSize.Height)

     Hope that helps understand it a little.  8)


    If you say it can`t be done then i`ll try it

    Monday, December 18, 2017 5:18 PM
  •  You're Welcome.  8)

    If you say it can`t be done then i`ll try it

    Monday, December 18, 2017 6:39 PM

All replies

  • Could somebody possible talk me through why it is I'm getting these bugs/ point me in the right direction; I'm not understanding why it's not working.

    Insert a breakpoint at the start of the routine so you can see what is being calculated.  The problem is likely in the calculation of font_height, because that seems to require at least three lines of text.

    Sunday, December 17, 2017 9:24 PM
  • Dim font_height As Single
                font_height = .GetPositionFromCharIndex(.GetFirstCharIndexFromLine(2)).Y _
                - .GetPositionFromCharIndex(.GetFirstCharIndexFromLine(1)).Y
                If font_height = 0 Then Exit Sub

    Cheers Acamar,

    I've tried fiddling about with the values in the font_height calculation, however I can't seem to find a solution. Are you sure that's what's up with this code?

    Thanks, and sorry for the late reply.

    Sunday, December 17, 2017 10:42 PM
  • Hi

    Tried your code. It seems to work OK here.

    I did make one small minor change (nothing to do with your problem), which was to start the line counter 'i' in the DrawRichTextBoxLineNumbers sub routine, with Dim i As Integer = first_line + 1.

    This was only to help eliminate a spurious line number zero from appearing at times.

    I couldn't reproduce the problems you speak of.


    Regards Les, Livingston, Scotland

    Sunday, December 17, 2017 10:54 PM
  • I've tried fiddling about with the values in the font_height calculation, however I can't seem to find a solution. Are you sure that's what's up with this code?

    When you inserted the breakpoint, and you had less than three lines of text in the RTB, what was the value calculated for font_height?  Was it the same as when you had three lines or more of text.

    The code you have posted calculates the difference in Y value of the position of the first character in lines 2 and 3 of the text.  So if you don't have three lines the calculation will be wrong, and the subsequent numbering will be wrong.

    In any case, I believe that code should be changed so that the height should be calculated from lines 1 and 2 of the text, not 2 and 3.  Then you need to add code so that if there is only 1 line of text the height value is not calculated and is not required.

    Sunday, December 17, 2017 11:07 PM
  • I am with Leshay I don't have a problem.

    Are you typing into the rtb or cut/paste or some other thing?

    Sunday, December 17, 2017 11:24 PM
  • @Ieshay,

    Thanks for that small fix. It's strange how I have the problem and you do not. I even changed the font to the default font (Tahoma 8.25). Are you absolutely sure you cannot reproduce the following problems:

    Please tell me whether or not you get these problems or not, because if you don't, then it might be a problem with my computer (I can't think how though)...

    Sunday, December 17, 2017 11:35 PM
  • @Ieshay,

    Thanks for that small fix. It's strange how I have the problem and you do not. I even changed the font to the default font (Tahoma 8.25). Are you absolutely sure you cannot reproduce the following problems:

    Please tell me whether or not you get these problems or not, because if you don't, then it might be a problem with my computer (I can't think how though)...

    Hi

    No, I do not get that behavior. I get the line numbers appearing correctly when enter is pressed. The line numbers are removed correctly too.


    Regards Les, Livingston, Scotland

    Sunday, December 17, 2017 11:40 PM
  • @Acamar,

    That makes sense then as it explains why the author of the code included:

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            RichTextBox1.Text = vbCrLf & vbCrLf & vbCrLf
        End Sub


    But that's just a cheap fix.
    Sunday, December 17, 2017 11:40 PM
  • @Ieshay, What font were you using in your textbox, if I may ask?
    Sunday, December 17, 2017 11:42 PM
  • @Ieshay,

    Thanks for that small fix. It's strange how I have the problem and you do not. I even changed the font to the default font (Tahoma 8.25). Are you absolutely sure you cannot reproduce the following problems:

    Please tell me whether or not you get these problems or not, because if you don't, then it might be a problem with my computer (I can't think how though)...


    Ok now I see the problem.
    Sunday, December 17, 2017 11:43 PM
  • @Ieshay, What font were you using in your textbox, if I may ask?

    Hi

    I just used the default for the RTB - Microsoft Sans Serif, 8pt

    EDIT: tried a few different font/sizes and all worked equally well.


    Regards Les, Livingston, Scotland


    • Edited by leshay Sunday, December 17, 2017 11:45 PM
    Sunday, December 17, 2017 11:44 PM
  • @Tommy,

    How strange...

    I'm typing normally. I run the project and just type...

    I have no other code that might interfere with this.

    In any case, I'll try creating a new project and see if I have the same issue.

    Thanks for trying it out.

    Sunday, December 17, 2017 11:44 PM
  • @Tommy

    Oh good, it's at the start. All lines past 3 work flawlessly, you have to look carefully to see the problem. Glad it wasn't just me - makes the problem much easier to resolve (probably).

    Sunday, December 17, 2017 11:46 PM
  • @Ieshay, What font were you using in your textbox, if I may ask?

    Hi

    I just used the default for the RTB - Microsoft Sans Serif, 8pt

    EDIT: tried a few different font/sizes and all worked equally well.


    Regards Les, Livingston, Scotland



    So when you run the project, by default, in the column there's the number "1"? If you press enter, does the number "2" appear? If you then clear all of those lines, is the number "1" in the column?
    Sunday, December 17, 2017 11:48 PM
  • Put at least in top of your code Option Strict On and correct than this line

     first_index = .GetCharIndexFromPosition(New Point(0, CInt(g.VisibleClipBounds.Y + font_height / 3)))


    Success
    Cor

    Sunday, December 17, 2017 11:49 PM
  • @Tommy,

    How strange...

    I'm typing normally. I run the project and just type...

    I have no other code that might interfere with this.

    In any case, I'll try creating a new project and see if I have the same issue.

    Thanks for trying it out.

    I am reproducing it.

    Type the first line, press enter, the lines are 1, 0 ... should be 0, 1. Any combination ie backspace to one line same. Like Acamar says its one of those step through one line at a time looking at the variables.

    Sunday, December 17, 2017 11:49 PM
  • But that's just a cheap fix.
        ... which means the code will fail if you delete lines, or paste text over existing lines, etc.
    Sunday, December 17, 2017 11:55 PM
  • Put at least in top of your code Option Strict On and correct than this line

     first_index = .GetCharIndexFromPosition(New Point(0, CInt(g.VisibleClipBounds.Y + font_height / 3)))


    Success
    Cor


    This doesn't have any effect on the bugs I'm afraid :\. Thanks very much for the suggestion though.
    Monday, December 18, 2017 12:12 AM
  •  Perhaps this is what you are trying to do...  The PictureBox BorderStyle is set to BorderStyle.None.

    Public Class Form1
        Private Sub RichTextBox1_Resize(sender As Object, e As EventArgs) Handles RichTextBox1.Resize
            PictureBox1.Height = RichTextBox1.Height
        End Sub
    
        Private Sub RichTextBox1_VScroll(sender As Object, e As EventArgs) Handles RichTextBox1.VScroll
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub RichTextBox1_TextChanged(sender As Object, e As EventArgs) Handles RichTextBox1.TextChanged
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
            DrawLineNumbers(e.Graphics)
        End Sub
    
        Private Sub DrawLineNumbers(g As Graphics)
            Dim linenumbers As Integer = RichTextBox1.GetLineFromCharIndex(RichTextBox1.TextLength)
            For i As Integer = 0 To linenumbers
                Dim RtbBorderSize As Integer = (RichTextBox1.Height - RichTextBox1.ClientSize.Height) \ 2
                Dim y As Integer = RichTextBox1.GetPositionFromCharIndex(RichTextBox1.GetFirstCharIndexFromLine(i)).Y
                Dim r As New Rectangle(PictureBox1.ClientSize.Width - 100, y + RtbBorderSize, 100, RichTextBox1.Font.Height)
                TextRenderer.DrawText(g, i.ToString, RichTextBox1.Font, r, Color.White, TextFormatFlags.VerticalCenter Or TextFormatFlags.Right Or TextFormatFlags.SingleLine)
            Next
        End Sub
    End Class
    
     

     


    If you say it can`t be done then i`ll try it

    Monday, December 18, 2017 5:38 AM
  • Thanks Razerz,

    That's almost exactly what I'm trying to do, however, I need the numbers to start from "1". Apart from that, that's perfect!!!Do you know of a way to make the number count start from "1"?

    Thanks a lot for your help with this.

    [EDIT]: I'm just reading over the code, trying to understand it the best I can and learn from it. I'm just curious, why did you divide the RtbBorderStyle int by 2? Thanks. 8)

    Monday, December 18, 2017 2:57 PM
  •  If you look in the DrawLineNumbers sub of my example,  you will see that the line numbers are draw by using the i value of the For Next loop which starts at 0.  You can just add 1 to the number as it is being drawn by changing the line below,  as shown.

    TextRenderer.DrawText(g, (i + 1).ToString, RichTextBox1.Font, r, Color.White, TextFormatFlags.VerticalCenter Or TextFormatFlags.Right Or TextFormatFlags.SingleLine)
     

     

     

     The reason for dividing the RtbBorderSize by two is because,  the RichTextBox has a Fixed3D border style (3 pixels wide) and the PictureBox has no border because it's border style is set to None,  no border.  In order to make the drawn line numbers line up with the lines in the RichTextBox,  you need to add just the top border size of the RichTextBox to the Y location of the line numbers when they are drawn.

     The part (below) of the line in question,  is taking the full height of the RichTextBox (including top and bottom borders) and subtracting the ClientSize height of the RichTextBox (no borders included),  from it.  That leaves you with the size of the Top and Bottom borders together (as one number).  That has to be divided by 2,  to get the size of just one border which is what you need to add to the Y location of the line numbers when drawing them.

    (RichTextBox1.Height - RichTextBox1.ClientSize.Height)

     Hope that helps understand it a little.  8)


    If you say it can`t be done then i`ll try it

    Monday, December 18, 2017 5:18 PM
  • Awesome, I understand it now. It works great too!

    Thanks Razerz and everyone else. 8)

    Monday, December 18, 2017 5:46 PM
  •  You're Welcome.  8)

    If you say it can`t be done then i`ll try it

    Monday, December 18, 2017 6:39 PM