
Introduction
This article demonstrates how to add line numbers to a RichTextBox control. This is done by adding a PictureBox control on the left side of the rich text box control. When the appropriate events happen on the Form or on the RichTextBox control, we call a method that paints the line numbers on the PictureBox control. This approach is more accurate and efficient than other approaches I've seen where another textbox control is used for printing the line numbers. The PictureBox will only print the line numbers that are currently visible in the RichTextBox control. During the process of constructing this code sample, I initially based it on the code sample provided in the article: http://www.codeproject.com/cs/miscctrl/numberedtextbox.asp and enhanced it by doing the following:
- Replace the
Label control for numbering with a PictureBox.
- I fixed the scrolling problem with that implementation due to the fact that the
RichTextBox uses smooth scrolling (scrolling with the scrollbar scrolls the text in pixels, not in lines). As a result, sometimes the first line may be displayed in half, in which case, the line numbering should address it.
Here is the code for printing the line numbers. Note that we have a RichTextBox control called MyRichTextBox and a PictureBox control called MyPictureBox. The method gets an argument which is the Graphics of the PictureBox control. This is the method DrawRichTextBoxLineNumbers:
Private Sub DrawRichTextBoxLineNumbers(ByRef g As Graphics)
With MyRichTextBox
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
g.Clear(Control.DefaultBackColor)
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)
g.DrawString((i).ToString, .Font, Brushes.DarkBlue, MyPictureBox.Width _
- g.MeasureString((i).ToString, .Font).Width, y)
i += 1
Loop
End With
End Sub
This method should be called from the following locations:
- Upon
RichTextBox's Resize event: DrawRichTextBoxLineNumbers(MyPictureBox.CreateGraphics).
- When the
VScroll event happens on the RichTextBox: (MyRichTextBox.VScroll).DrawRichTextBoxLineNumbers(MyPictureBox.CreateGraphics).
- When the
Paint event happens: (MyPictureBox.Paint).DrawRichTextBoxLineNumbers(e.Graphics). Note that it this method, we send e.Graphics and not MyPictureBox.CreateGraphics, as we would like to repaint only the required region within the PictureBox control.
These are the locations from which we need to invoke the line numbering method:
Private Sub r_Resize(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles MyRichTextBox.Resize
MyPictureBox.Invalidate()
End Sub
Private Sub r_VScroll(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles MyRichTextBox.VScroll
MyPictureBox.Invalidate()
End Sub
Private Sub p_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyPictureBox.Paint
DrawRichTextBoxLineNumbers(e.Graphics)
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
MyRichTextBox.Text = vbCrLf & vbCrLf & vbCrLf
End Sub
Other general notes
- The code above assumes that the font name and the size of the
RichTextBox control is the same for the entire text.
- The
WordWrap property of the RichTextBox control should be set to False.
- I've placed this code in the
Form_Load method to assure that the textbox has at least two lines always:
MyRichTextBox.Text = vbCrLf & vbCrLf & vbCrLf
- I recommend docking the
PictureBox to the left and then calculating the width of the PictureBox based on g.MeasureString("000", MyRichTextBox.Font).Width. The width of the PictureBox should be recalculated, and the PictureBox should be redrawn when the font of the RichTextBox changes.
- The
PictureBox control may be replaced with a Panel or any other control that exposes the CreateGraphics() method.
- The control was designed for
RichTextBox controls with WordWrap = False.