Click here to Skip to main content
Click here to Skip to main content

LineNumbers for the RichTextBox

By , 31 May 2007
 
Screenshot - linenumbers_for_rtb_examples.jpg

Introduction

Although there are already LineNumbering controls around, I decided to code one that gives the user a lot of freedom to create an individual look, whilst still handling the RichTextBox's dynamic content correctly. There is also a SeeThroughMode that allows the LineNumbers to be displayed as an overlay on top of the RichTextBox itself. Word wrapping and differences in line heights are all properly considered and, as the control only paints LineNumberItems for the text lines that are visible, the painting speed remains high even for large pieces of text with complex layout.

Screenshot - linenumbers_for_rtb_overlay.jpg

Using the code

The download ZIP file contains the VB.NET solution folder. All of the code for the LineNumbers control is in the LineNumbers_For_RichTextBox class-code. Use the Solution Explorer to find it once you've opened the LineNumbers.sln project file. Make sure you build the project before opening the form or you'll get an error message. If that happens, close the Form's design tab and rebuild the solution. All of the code is provided "as is," with no rights nor liabilities attached. This means that you can use and change it as you please, at your own risk.

Copy the class-code into your project, and build or rebuild your application/solution. The LineNumbers control should then be available in your Toolbox. Once you've added the LineNumbers control to your form, you'll notice that it displays a vertical reminder message: you need to set the ParentRichTextBox property first so that it knows which RTB to show the LineNumbers for. Once it's set, the LineNumbers control will dock to the left side of the RTB -- controlled by the DockSide property -- and will either start showing the line numbers if there is already text in the RTB, or another reminder that shows which RTB it is connected to.

Available properties

You can use the following elements to customize the look of the LineNumbers. All lines can have their Color, LineStyle (dot, dash, solid, etc.) and LineThickness changed. This LineNumbers control inherits from the basic Control class, so a BackgroundImage can also be set.

BorderLines

Element that defiones the border around the whole control.

GridLines

Element that defines a horizontal divider line across the top of each LineNumber's item-area.

MarginLines

Element that defines a border line that can appear on the left, right, or both vertical sides of the control.

BackgroundGradient

Each LineNumber's item-area can have a gradient that softly blends two colors, named alpha and beta color in the public properties and Start/EndColor in the code. All colors can be transparent and you can also specify the gradient's direction, i.e. horizontal, vertical, forward/backward-diagonal. It's drawn via a Drawing2D.LinearGradientBrush. See code snippet below:

' --- BackgroundGradient
If zGradient_Show = True Then
   zLGB = New Drawing2D.LinearGradientBrush(zLNIs(zA).Rectangle, _
              zGradient_StartColor, zGradient_EndColor, zGradient_Direction)
   e.Graphics.FillRectangle(zLGB, zLNIs(zA).Rectangle)
End If

LineNumbers

The LineNumbers' color and font is set via the normal ForeColor and Font properties, but there are also extra properties available to change their look and behavior:

  • LineNrs_Alignment: by which you can set the alignment point (TopLeft, TopCenter, TopRight, ...) for the LineNumber so that the number is drawn relative to that corner/center-point of its item-area. This is the same as the TextAlign property on a regular Label.
  • LineNrs_LeadingZeroes: pads the LineNumber with leading zeroes, based on the total amount of text lines in the RichTextBox.
  • LineNrs_AsHexadecimal: shows the LineNumbers as hexadecimal values (i.e. no leading zeroes in that case).
  • LineNrs_Anti-Alias: some fonts look better when the edges of the text-characters are slightly blended with the background. However, other fonts may look crisper without that softening, especially small pixel fonts.
  • LineNrs_ClippedByItemRectangle: if the LineNumbers are using a large font, they may spill out of their own item-area. This can sometimes give cool effects in combination with a partially transparent BackgroundGradient. This option allows you to clip the LineNumbers so that they only appear inside their own area.
  • LineNrs_Offset: although the alignment will take care of the LineNumber placement, this property allows you to manually fine-tune the LineNumber's position. Use negative values for offsets towards the TopLeft, and positive values to shift the position of the LineNumbers towards the BottomRight.

LineNumbers_For_RichTextBox

The behavior of the LineNumbers_For_RichTextBox control is governed by these properties:

  • ParentRichTextBox: this needs to be set first, as it allows you to point to the RichTextBox control for which the LineNumbers will be displayed. In design mode, a vertical reminder message will show up when the parent RTB is not set, or when the RTB has no text in it yet.
  • _SeeThroughMode_: the LineNumbers control can either be displayed next to its parent RichTextBox, or it can be displayed as an overlay on top of the RTB. The empty parts of the LineNumbers are then both see-through and click-through, so you can still use the RTB underneath.
  • AutoSizing: when active, auto-sizing will automatically adjust the width and position of the LineNumbers control as needed in order to make sure that the LineNumbers remain visible.
  • DockSide: you can use this to dock the LineNumbers to the left or right side of the parent RTB, or to lock the height to that of the RTB. When set to none, you can position the LineNumbers control freely like any other. The standard Dock will override the DockSide behavior, though.

Points of interest

Although this is a pretty straightforward control designed to do just one thing, there were a few problems that needed some attention to get the control working at a good speed. The central Sub, which is Update_VisibleLineNumberItems() takes care of several of them. The rest of the work is mostly being done by the overridden OnPaint sub.

Lining up the LineNumbers and RTB text lines

The RichTextBox has an easy GetPositionFromCharIndex() method that computes the position of a given text character -- identified by its index within the full text -- but that position point is in client-coordinates. So, at the start of the Update_VisibleLineNumberItems() you can see some conversions to screen coordinates and back, to determine where the RTB's (0,0) origin point is in the LineNumbers control. Also, there is an additional check to find the control's (0,0) origin point within the parent RTB because the LineNumber control's Top may be positioned lower on the form than the RTB. That would make a difference in the computation of which text lines should get a LineNumberItem drawn for them, as only visible LineNumberItems should matter, to keep things speedy. The Update_VisibleLineNumberItems() sub basically builds a list, named zLNIs, of only the visible LineNumberItems. Each LineNumberItem (which is a Structure Update B: this is now a nested class) holds a LineNumber and a rectangle that marks the LineNumber's item-area.

WordWrapping and LineHeight

The main problem was the fact that when word wrapping splits a text line into multiple lines, those new text lines spill into the RichTextBox's Lines collection -- this happens on a regular TextBox, as well -- without actually adding items to the collection. For example, an RTB with 5 real text lines and word wrapping disabled will have a correct Lines collection of 5 items where each item is a real text line. But when word wrapping is enabled and happens to wrap the first real text line into 2 lines, then the Lines collection will still have 5 items, but item2 will be the word-wrapped second half of the first real text line. To counter that peculiar behavior, the LineNumbers control needs to create its own Lines collection, one that isn't affected by the word wrapping and that the real text lines. This is the zSplit list of strings in the Update_VisibleLineNumberItems() sub. The line-height (i.e. the height of the LineNumberItem's rectangle) will be computed by comparing the Y-coordinate of each real text line with that of the next real line. The GetPositionFromCharIndex() method will give us the Y-coordinates, but the char index of the first character of each visible text line needs to be known.

Computing which LineNumbers are visible

The control needs to find out which text lines in the RTB need to have a LineNumberItem drawn for them. Only visible items should be drawn to keep the painting speed high. The initial value of the zStartIndex variable, which is the char index of the first (fully or partially) visible text character will be computed by the FindStartIndex() sub. It's a recursive sub (i.e. one that calls itself) that basically looks for a text character that has a Y-coordinate closest to 0 or closest to the target value. The code comments will explain how it's done exactly.

The painting of the LineNumbers (just the numbers)

Here's a code-snippet that shows the painting of the LineNumbers in the overriden OnPaint sub. The large TextAlignment computations that determine zPoint are left out, though. You can see how the text clipping is done by using the Graphics.SetClip method to temporarily restrict the drawing area. Also notice that a rectangle, zItemClipRectangle, based on the LineNumber's text-dimensions (clipped or not) is added to the zGP_LineNumbers object. This is a GraphicsPath object that will be used in SeeThroughMode. More on that is to be found in the next article section.

' --- LineNumbers
If zLineNumbers_Show = True Then
    '   TextFormatting
    If zLineNumbers_ShowLeadingZeroes = True Then
        zTextToShow = IIf(zLineNumbers_ShowAsHexadecimal, _
            zLNIs(zA).LineNumber.ToString("X"), _
            zLNIs(zA).LineNumber.ToString(zLineNumbers_Format))
    Else
        zTextToShow = IIf(zLineNumbers_ShowAsHexadecimal, _
            zLNIs(zA).LineNumber.ToString("X"), _
            zLNIs(zA).LineNumber.ToString)
    End If
    '   TextSizing
    zTextSize = e.Graphics.MeasureString(zTextToShow, Me.Font, zPoint, zSF)

    ' ==TextAlignment computation here (large Select Case to build zPoint)==
    
    '   TextClipping
    zItemClipRectangle = New Rectangle(zPoint, zTextSize.ToSize)
    If zLineNumbers_ClipByItemRectangle = True Then
        '   If selected, the text will be clipped so that it doesn't spill out
        '   of its own LineNumberItem-area. Only the part of the text inside 
        '   the LineNumberItem.Rectangle should be visible, so intersect with 
        '   the ItemRectangle.
        '   The SetClip method temporary restricts the drawing area of the 
        '   control for whatever is drawn next.
        zItemClipRectangle.Intersect(zLNIs(zA).Rectangle)
        e.Graphics.SetClip(zItemClipRectangle)
    End If
   
    '   TextDrawing
    e.Graphics.DrawString(zTextToShow, Me.Font, zBrush, zPoint, zSF)
    e.Graphics.ResetClip()   
   
    '   The GraphicsPath for the LineNumber is just a rectangle behind the 
    '   text, to keep the paintingspeed high and avoid ugly artifacts.
    zGP_LineNumbers.AddRectangle(zItemClipRectangle)
    zGP_LineNumbers.CloseFigure()
End If

SeeThroughMode

I can imagine people being interested in this, as it's a little more advanced than the simple painting of lines and rectangles. So, here's some information on how it's done: it works by using a Drawing2D.GraphicsPath object, which is similar to the more regularly used Graphics type. However, when you paint something on a GraphicsPath, you're basically painting which pixels will be see-through or not when that GraphicsPath -- or a combination of several GraphicsPaths, in this case -- is set as the Region of the control. In other words, you're creating a custom outline for the control so that you can make the control any shape you like, even with holes in it if needed.

I'm doing the painting on the GraphicsPaths at the same time as the regular painting in the overridden OnPaint sub. This is because the lines and rectangle figures are being computed anyway, so I might as well use them twice. The code-snippet below shows this clearly: the same border lines that are drawn on the regular Graphics (e.Graphics.DrawLines ...) are also drawn onto a GraphicsPath (zGP_BorderLines.AddLines...):

Dim zGP_BorderLines As New Drawing2D.GraphicsPath(Drawing2D.FillMode.Winding)

Dim zP_Left As New Point(Math.Floor(zBorderLines_Thickness / 2), _
    Math.Floor(zBorderLines_Thickness / 2))
Dim zP_Right As New Point(
    Me.Width - Math.Ceiling(zBorderLines_Thickness / 2), _
    Me.Height - Math.Ceiling(zBorderLines_Thickness / 2))

' --- BorderLines 
Dim zBorderLines_Points() As Point = { _
    New Point(zP_Left.X, zP_Left.Y), _
    New Point(zP_Right.X, zP_Left.Y), _
    New Point(zP_Right.X, zP_Right.Y), _
    New Point(zP_Left.X, zP_Right.Y), _
    New Point(zP_Left.X, zP_Left.Y)}
If zBorderLines_Show = True Then
   zPen = New Pen(zBorderLines_Color, zBorderLines_Thickness)
   zPen.DashStyle = zBorderLines_Style
   e.Graphics.DrawLines(zPen, zBorderLines_Points)

   '   And the same shape is added to the border's GraphicsPath
   zGP_BorderLines.AddLines(zBorderLines_Points)
   zGP_BorderLines.CloseFigure()

   '   BorderThickness and Style for SeeThroughMode
   zPen.DashStyle = Drawing2D.DashStyle.Solid
   zGP_BorderLines.Widen(zPen)
End If

At the end of the OnPaint sub, the control simply checks whether zSeeThroughMode is active. If it is, then the different GraphicsPaths (named zGP_...) are combined and form the control's Region after an extra check is done, to make sure the control won't be empty:

' --- SeeThroughMode
'   combine all the GraphicsPaths (= zGP_... ) and set them as the Region 
If zSeeThroughMode = True Then
    zRegion.MakeEmpty()
    zRegion.Union(zGP_BorderLines)
    zRegion.Union(zGP_MarginLines)
    zRegion.Union(zGP_GridLines)
    zRegion.Union(zGP_LineNumbers)
End If

' --- Region
If zRegion.GetBounds(e.Graphics).IsEmpty = True Then
    '   Note: If the control is in a condition that would show it as empty, 
    '   then a border-region is still drawn regardless of it's borders' 
    '   on/off state. This is added to make sure that the bounds of the 
    '   control are never lost (it would remain empty if this was not done).
    zGP_BorderLines.AddLines(zBorderLines_Points)
    zGP_BorderLines.CloseFigure()
    zPen = New Pen(zBorderLines_Color, 1)
    zPen.DashStyle = Drawing2D.DashStyle.Solid
    zGP_BorderLines.Widen(zPen)

    zRegion = New Region(zGP_BorderLines)
End If
Me.Region = zRegion

Updates

Fixed:

  • (A) When the first LineNumberItem had a negative Y-coordinate, the bottom line of the rectangle for the GridLines' GraphicsPath would show inside the control. Offsetting by -zLNIs(0).Rectangle.Y has fixed this.

Improved:

  • (B)Performance has been doubled by increasing the efficiency of the Update_VisibleLineNumberItems() method. This was achieved by halving the number of calls to the RTB's .GetPositionFromChar() method, which becomes slower as the number of text lines grows.
  • (B) Scrolling of large documents now has a time-based cutoff for computing LineNumberItems so that scrolling remains smooth.

The end

That's it, I hope you like this LineNumbers_For_RichTextBox control and find it useful in your own projects. Enjoy! --- nogChoco

History

  • 31 May, 2007 -- Article edited and moved to the main CodeProject.com article base
  • 12 April, 2007 -- Updated
  • 5 April, 2007 -- Original version posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

nogChoco
Belgium Belgium
Member
MSDN VB.NET Forums

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionAwesomememberYanick Lafontaine22 Feb '13 - 7:21 
Very good job, totally awesome.
AnswerRe: AwesomemembernogChoco4 Mar '13 - 7:25 
I'm happy you like it -- Thank you kindly, Yanick Lafontaine Smile | :)
SuggestionExcellent work, but PLEASE release under different licencememberMember 973382010 Jan '13 - 16:50 
The CPOL licence under which this code is currently released is incompatible with the Free Software Foundation's GPL licence and it is not approved by the Open Source Initiative. Long story short this means that your excellent code cannot be used (legally) in 99% of all of the open source projects out there, virtually all of which use either GPL or an OSI licence. Cry | :((
 
The reason that the CPOL is not comparable with standard open source licences is that it contains crazy restrictions such as "You agree not to use the Work for illegal, immoral or improper purposes" - what's an immoral purpose? This prohibition basically makes the licence useless, because no two people will ever agree on what an immoral or improper purpose is.
 
If you want as many people as possible using your code please consider releasing it under one of the standard open source licences, the two most commonly used ones are GPL and BSD.
 
While there are many excellent articles online discussing the differences between the two, the bottom line is that you want to choose the GPL if you want your code to only be used in open source software and you want to use the BSD if you want everyone including businesses that make proprietary software to be able to use your code.
 
Thanks for writing this control. I hope you will release it under a standard open source licence of your liking.
GeneralMy vote of 5memberZangetsuZ30 Dec '12 - 8:55 
very usefull thanks a lot Smile | :)
GeneralRe: My vote of 5membernogChoco8 Jan '13 - 5:04 
I'm glad you like it -- thanks, ZangetsuZ Smile | :)
QuestionVery good and practicable...memberdherrmann29 Oct '12 - 23:48 
Hi,
 
I use this control very often and I like it!
But, a question:
Do you have any idea, if you can enhance the control for the using with TextBox or/and ListBox too?
 
Regards
Dietrich
AnswerRe: Very good and practicable...membernogChoco6 Nov '12 - 2:29 
Hi dherrmann Smile | :) I'm glad you like it!
 
I guess it shouldn't be too hard to adapt the code so that a regular TextBox can use it too, but a TextBox' lineheights are always the same (as with a ListBox) so a significant part of the code would no longer be needed, I think.
 
The lineheights and the location of the parentcontrol are really the only things that need to be given to the linenumbering control.
QuestionGreat workmembernguyenphuphi6 Dec '11 - 11:43 
Just want to comment on your great work. I like the fact that it is a separate control to the RichTextBox!
AnswerRe: Great workmembernogChoco3 Jan '12 - 8:01 
Thanks, nguyenphuphi, I'm glad you like it Smile | :)
QuestionLooks great...memberMember 425354611 Apr '10 - 7:09 
...too bad I can not use it.
 
Because I dont have any .NET. Only Visual Basic 6...
 
I've been working on a code editor in Visual Basic 6 for several months, and it's almost finished, except that I dont have a proper linenumbering feature yet. I have had a hard time trying to apply line numbering to the RTB in a way that looks good. I've tried many different things, including sub classing and painting linenumbers in a picturebox, but due to the smooth scrolling of RTB, it doesnt align properly when scrolling etc.
 
I wonder, is it possible to port your linenumber class to Visual Basic 6?
 
I would be very happy if you could help me solve my problem. (Banging my head into the wall for months trying to fix the linenumbering, is not fun)
 
Thanks in advance!
 
Yours Sincerly,
Nosferatu
AnswerRe: Looks great...membernogChoco15 Apr '10 - 15:06 
Hi Nosferatu,
 
Unfortunately, I never really used VB6 (I coded mostly in VBA for AutoCad) so I have no idea of what parts of the code can be backported to it Frown | :(
 
A large part of the logic in my program is based on working around the RTB's quirky/flawed .lines array which becomes incomplete when lines of text are wrapped. A VB6 RTB may not even have those quirks.
 
I believe the coordinate system in VB6 differs from that in VB.NET so if you're trying to use that part of my code directly, it may cause misalignment.
 
This program was a learning project for me too, so if I were to redo it today, it would probably look even more .NET-ish than back when I made it.
 
I'm assuming you know most of these, but mentioning them anyway: VB6 resources[VB6 resources]
GeneralRe: Looks great...memberNosferatu The Prince of Darkness17 Apr '10 - 15:11 
Hi nogChoco
 
Thanks for the reply. I know most of the VB6 Resources, and one will mostly find newbie questions there (like how to change the color of a label or simular). I've searched and searched. Found a few bad attempts to add Linenumbers to RTB, which does not work, and by the way look ridiculous.
 
The RTB for Visual Basic 6, does not have any linenumbers, or much else. I've read that Microsoft attempted to add more features to the .Net RTB, including linenumbers, but that they left it half finished, and that's probably what you mean by "quirky/flawed".
 
Is your code dependent on that quirky .Net RTB, or could one use a VB6 RTB without any linenumber feature?
 
I have a feeling that VB6 is very limited compared to the .Net version, and that it may be impossible to port all of your code (and features) to VB6, but I've started an attempt to port it now. And I think I found a small bug in your code (the linenumber class):
 
        
        ' --- Memory CleanUp
        If zPen IsNot Nothing Then zPen.Dispose()
        If zBrush IsNot Nothing Then zPen.Dispose()
 
Shouldnt it be:
        ' --- Memory CleanUp
        If zPen IsNot Nothing Then zPen.Dispose()
        If zBrush IsNot Nothing Then zBrush.Dispose()
 

Anyway, I wonder if you are willing to assist me in the porting attempt, and if you can email me or something.
 
Thanks in advance.
 
Yours sincerly,
Nosferatu
GeneralRe: Looks great...membernogChoco18 Apr '10 - 4:25 
Ah, yes, that looks like a copy/paste bug Blush | :O
 
I never worked with classes or graphics in VBA (as the output in AutoCAD was graphical anyway), so I don't think I can be much help to you, to be honest.
 
In VB.NET, the RTB gives the location for each line and each character's top-left corner. If the VB6 one does that too, then you should be able to do what I did, although probably with less fancy graphics as .NET uses GDI+, and perhaps the drawing and RTB's location-polling speeds may not be as fast.
 
The quirky .Lines array meant I couldn't just trust the position of the .Lines and had to find the first character of each real line myself. Looking back at the code, it's probably not that big a deal though it likely took me the most thought (counting linebreaks to find the right line instead of just using .Lines).
 
In essence, the RTB and LineNumbers are only linked by computing where the .Y location of the RTB's first character is, in the LineNumbering control. I compute that by converting the RTB's coordinates to screencoordinates and then to the LineNRs coordinates, but as I said: the coordinate system is different in VB6 so that will probably need rethinking - it's possible that you don't even have to convert and can just use the same .Y height in both RTB and LineNRs.
 
After you have that .Y offset, it's just a matter of getting the position of each visible RTB line's first character and drawing a LineNR on the same height.
GeneralGreat WorkmemberAnt210010 Aug '09 - 8:22 
Great! I love it! Thanks for this control Smile | :)
 
Check out my desktop conversion software for Windows -
 
www.universalconverter.net

GeneralRe: Great WorkmembernogChoco13 Aug '09 - 11:07 
Thank you for your compliment, Ant2100 -- enjoy the code Smile | :)
GeneralAnother "great job" comment :o)memberRobW28 Jul '09 - 1:26 
Compiled to assembly to include in a C# project and it all just worked, no fuss. Simple and elegant.
 
Got to be one of the most useful controls on CodeProject.
 
Thanks!
 
Rob
GeneralRe: Another "great job" comment :o)membernogChoco13 Aug '09 - 11:06 
Hi Rob, thank you for your compliment -- I'm happy you found it a useful control Smile | :)
QuestionC# Equivalent?memberCaglow12 Oct '08 - 6:49 
Excellent article! Anyone know of a C# equivalent of this though? I find it very interesting but unless it can be converted to C# (I've made many attempts and failed), I won't be able to use it.
 
Anyways, great job.
AnswerRe: C# Equivalent?memberVincent Meijer28 Nov '08 - 5:38 
I've converted this code to C# and use in some of my projects.
You can find the converted code in this [article]. Just download the source and take a look at the LineNumbers.cs file.
GeneralRe: C# Equivalent?memberMember 441891016 Nov '10 - 3:57 
the link u gave is not working for c#..
can u pls help me
GeneralRe: C# Equivalent?memberantgraf30 Oct '11 - 9:18 
https://github.com/antgraf/C--Numbered-Lines-Control-for-RichTextBox--LineNumbersControlForRichTextBox-
GeneralI don't understand the Part beginning "Using the Code" [modified]memberRogerBerglen20 Aug '08 - 7:08 
I have under VB2008 Express a large Project with Multitab-Editor. I'm reading your Text and I don't check the second Block of "Using the Code".
1. I have unzipped the Files and i'm copy the File: LineNumbers_For_RichTextBox.vb in my Project.
2. I have build my Project by click (I have the German Version of VB2008) "Erstellen, MLT2007 Erstellen". After the Build i can't see any Change in the Toolbox!
When i add the Class-Code via "Project, existing Element add" the VB2008 Express displays two Errors.
Fehler 1 Der Typ "List" ist nicht definiert. LineNumbers_For_RichTextBox.vb 69 26 MLT-2007NET
 
Warnung 2 Name "_SeeThroughMode_" ist nicht CLS-kompatibel. LineNumbers_For_RichTextBox.vb 155 77 MLT-2007NET
 
In my existing Project i can't Insert your Class! The two Error-Messages are displayed.
When i create a new Project that i can Insert your Class. That's absolute bad! I can't start my Project new!
Exist this Control as the other Controls that i can Insert to the Tool-Box?
 
Can I create your Control under Runtime per Code? I create the RTF-Boxes under Runtime per Code and i don't have on my MDI-Window any RTF-Boxes when my Program starts. The RTF-Boxes are created on Tab-Registers on Runtime.
 
modified on Wednesday, August 20, 2008 4:31 PM

GeneralRe: I don't understand the Part beginning "Using the Code"membernogChoco21 Aug '08 - 18:00 
Hi RogerBerglen,
 
The easiest way to add it to your project, would be by selecting 'Add Class' from the Project menu, then copy/paste the code from LineNumbers_For_RichTextBox.vb into that new class tab (overwriting the small 'Public Class Class1, End Class' code that is already there), and then rebuilding the project.
 
When you then select a form's design tab, you should see a section in the toolbox called 'YourProjectName Components' and the 'LineNumbers_For_RichTextBox' will be listed there like a normal control (you can use it via code too).
 
The 'Der Typ "List" ist nicht definiert' error sounds like the 'System.Collections.Generic' namespace isn't imported into your project yet.
 
The second error about CLS-compatibility is hard for me to judge, as I assume it depends on how strict the compatibility checks are. The only thing different about that line 155, is the fact that it is written with underscores (I just did that to make it stand out in the property-listing). Maybe rename that _SeeThroughMode_ property (and each call to it)?
GeneralRe: I don't understand the Part beginning "Using the Code"memberRogerBerglen22 Aug '08 - 1:00 
I have Test to Add the Control on my Editor only per Code. And i can't not see the LineNumberControl when my Program run.
When i place the Control from the Toolbox on a RichTextBox then i can see the LineNumbers. What' wrong?
The Code:
 
Private Shared ZeilenNr(0) As LineNumbers_For_RichTextBox
 
Z = 0
 
For Z = 0 To SeitenDa.Length
If SeitenDa(Z) = False Then
A = Z
If Z + 1 = SeitenDa.Length Then
ReDim Preserve Edit(Edit.Length)
ReDim Preserve ZeilenNr(ZeilenNr.Length)
End If
Exit For
End If
Next
 
ZeilenNr(A) = New LineNumbers_For_RichTextBox
Edit(A) = New RichTextBox
 
Edit(A).Size = New Point(Rahmen(A).Width - 18, Rahmen(A).Height - 21)
Edit(A).Left = 12
Edit(A).Top = 14
Edit(A).Name = "Editor" + A.ToString
Edit(A).BorderStyle = BorderStyle.None
Edit(A).Multiline = True
Edit(A).ContextMenuStrip = Desktop.EditContext
Edit(A).Font = New Font("Courier New", 10)
Edit(A).AcceptsTab = True
 
Desktop.EditorTab.TabPages.Add((A).ToString, "Neu" + (A).ToString, 0)
Desktop.EditorTab.SelectTab(A)
Desktop.EditorTab.TabPages(A.ToString).Controls.Add(Edit(A))
Desktop.EditorTab.TabPages(A.ToString).Controls.Add(ZeilenNr(A))
ZeilenNr(A).ParentRichTextBox = Edit(A)
 
The Code put on a Tab a RichTextBox, a Frame around the Richtextbox and the LineNumberControl.
The LineNumberControl will be not displayed. Have i forgotten Anywhere??
Have you a Demo-Code to create your Control on Runtime on a Tab-Control?
GeneralRe: I don't understand the Part beginning "Using the Code"membernogChoco22 Aug '08 - 9:43 
It seems to be a combination of 2 problems with my control: the width wasn't being set properly (so width=0 and that made it lose its region, I think), and there is also an initial sizing that was forgotten - my control wasn't resizing until there was a contentresize on the parent RichTextBox.
 
For the width problem, simply put a "Me.Width = 1" line in the New() constructor so that it looks like this:
 Public Sub New()
        With Me
            .SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
            .SetStyle(ControlStyles.ResizeRedraw, True)
            .SetStyle(ControlStyles.SupportsTransparentBackColor, True)
            .SetStyle(ControlStyles.UserPaint, True)
            .SetStyle(ControlStyles.AllPaintingInWmPaint, True)
            .Margin = New Padding(0)
            .Padding = New Padding(0, 0, 2, 0)
        End With
        With zTimer
            .Enabled = True
            .Interval = 200
            .Stop()
        End With
        Me.Width = 1
        Me.Update_SizeAndPosition()
        Me.Invalidate()
    End Sub
 
And for the intial sizing problem, add a "zContentRectangle = zParent.ClientRectangle" line to the ParentRichTextBox property setter, so that it looks like this:
Public Property ParentRichTextBox() As RichTextBox
        Get
            Return zParent
        End Get
        Set(ByVal value As RichTextBox)
            zParent = value
            If zParent IsNot Nothing Then
                Me.Parent = zParent.Parent
                zContentRectangle = zParent.ClientRectangle
                zParent.Refresh()
            End If
            Me.Text = ""
            Me.Refresh()
            Me.Invalidate()
        End Set
    End Property
   
(and don't forget to leave some room next to your richtextbox so that the LineNumbers control can show up there)
 
That should solve the problem, I think Smile | :) Sorry for the inconvenience Blush | :O
GeneralRe: I don't understand the Part beginning "Using the Code"memberRogerBerglen22 Aug '08 - 11:27 
Thank you for the fast answer! That's a Service, Great! I will test your Codechanges. Im good Luck to see the Line-Numbers on my MultiTab-Editor.
GeneralRe: I don't understand the Part beginning "Using the Code"memberRogerBerglen22 Aug '08 - 11:57 
I write a simple Programregion in my Project. The Code place a Child-Window with a RichTextBox on my MDI-Window. Unfortunally the LineNumbers are not displayed ! I have made your Changes before i write the Test-Code.
Here is the Test-Code:
 
Public Class Editor
 

 
Private Sub Editor_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 
Dim Test As New LineNumbers_For_RichTextBox
 

Test.ParentRichTextBox = Me.RichTextBox1
Test.AutoSizing = True
Test.BackgroundGradient_AlphaColor = Color.White
Test.BackgroundGradient_BetaColor = Color.LightSteelBlue
Test.BackgroundGradient_Direction = Drawing2D.LinearGradientMode.Horizontal
Test.BorderLines_Color = Color.SlateGray
Test.BorderLines_Style = Drawing2D.DashStyle.Dot
Test.BorderLines_Thickness = 1
Test.DockSide = LineNumbers_For_RichTextBox.LineNumberDockSide.Left
Test.GridLines_Color = Color.SlateGray
Test.GridLines_Style = Drawing2D.DashStyle.Dot
Test.GridLines_Thickness = 1
Test.LineNrs_Alignment = ContentAlignment.TopRight
Test.LineNrs_AntiAlias = True
Test.LineNrs_AsHexadecimal = False
Test.LineNrs_ClippedByItemRectangle = True
Test.LineNrs_LeadingZeroes = True
Test.Visible = True
Me.Controls.Add(Test)
 

 

End Sub
End Class
GeneralRe: I don't understand the Part beginning "Using the Code"membernogChoco24 Aug '08 - 11:21 
After some testing, I think the problem occurs when there is not enough room to display the LineNumber items fully: if the control's location.X is negative, which means only a part of the linenumber-items would show, then the linenumber-item(s) are considered as totally invisible, and then the empty control no longer shows, of course. It only seems to happen at the initial sizing of the control, though, which is a bit weird.
 
I probably coded it that way to speed up the drawing of items, with this unfortunate side-effect as a result. For now, I can only suggest to leave enough room for the control, as I can't give you reworked code at the moment :(
GeneralThis is very cool!memberRens Duijsens7 Apr '08 - 4:38 
Thank you very much for this tool.
I was building it myself (and not succeeding very well) and by accident stumbled upon this.
 
Thank you very very much for this code.
I love it. Big Grin | :-D
GeneralRe: This is very cool!membernogChoco16 Apr '08 - 11:49 
Thank you for the compliments - Have fun with the code! Smile | :)
GeneralConversion in C#membernaveenrhl24 Dec '07 - 21:45 
Blush | :O
Hi,
Can you send its C# version on my mail : naveenrhl@yahoo.com
Thanks
Navin
 
Sniff | :^)
 
always care for physics

QuestionDelay while working with large filememberPRAVEEN KAUSHIK27 Sep '07 - 0:05 
Hi
 
I m using this code in my Code editor project, nice code. but i feels when I am working with large files then it delaying in refreshing and display line numbers.
 
can you help me how to over come this problem.
 
Regards
Praveen kaushik
AnswerRe: Delay while working with large filemembernogChoco27 Sep '07 - 21:38 
Hello,
 
Thank you for the compliment Smile | :)
 
Unfortunately, Update B is about as fast as I can make it because the delay is mostly caused by the underlying RichTextBox's GetPositionFromCharIndex() function which slows down considerably when large texts are used Frown | :(

QuestionAll Right!membercomputergeek10119 Sep '07 - 9:13 
I think this is exactly what I need for my text editor I was planning to build. Good Job! Smile | :) What about adding search function?
AnswerRe: All Right!membernogChoco22 Sep '07 - 21:26 
Hi computergeek101,
 
I have no further plans with this control, but you have the source-code so feel free to use it as you please Smile | :)

Questionone questionmemberCecil Cheah28 Jun '07 - 23:01 
This is by far the best line number control i have seen. However, i do have a question.
I am developing a Code Editor programme for my work, and one of the feature is use code folding. Now, how do i implement code folding with this control?
So, if i am to fold the code between line number 25 to 37 in my rtf control, so i only want to show line number 1 to 24, and 38 to say 50, how do i make the call to the line number control?
 
Thanks
 
Cecil
AnswerRe: one questionmembernogChoco3 Jul '07 - 11:47 
Hi and thanks, Cecil Smile | :)
 
Unless I'm mistaken, codefolding is not a part of a standard RTB control, and this LineNumber control is only built upon the standard properties.
 
I think it'd be easier to just make your own RTB. That way, you can correct the linebreak "bug" in the standard RTB which should make it far easier to compute the LineNumbers.
 
At the moment, I don't have time to help you write it, though Sigh | :sigh:
 

GeneralGreat..memberAmerican_Eagle5 Jun '07 - 11:46 
great..
GeneralRe: Great..membernogChoco6 Jun '07 - 9:48 
Thanks for your compliment, American_Eagle Smile | :)
Generalresize / scroll bugmemberfrumbert4 Jun '07 - 13:24 
I've found that if you resize the window to be smaller (e.g. make the window scroll to line 5 in your example) then the scrolling height doesn't change until you have dragged the scrollbar at least once, which then recalculates the correct scrolling height.
GeneralRe: resize / scroll bugmembernogChoco5 Jun '07 - 4:47 
Thanks for your feedback, frumbert Smile | :)
 
It seems to be a RichTextBox bug, though: I removed the linenumbers and the RTB's scrolling did not calculate properly either.

GeneralGreat Job!!memberVivek Krishnamurthy29 Apr '07 - 21:58 
Hi, First of all, its really cool control and great job!!..
and lots of thanks for sharing it.
 
I as looking for a similar control, but in C#. so I have converted your code to C# using a conversion util and did little fix after conversion. Also, I have included your name/article reference in Credits.
 

Thanks,
Vivek
GeneralRe: Great Job!!membernogChoco30 Apr '07 - 2:59 
Thanks for the compliment and the credits, Vivek Smile | :)
 

 


GeneralRe: Great Job!!memberr0dney7 May '07 - 12:24 
I also would like a C# version of this, is there any chance of you posting the conversion that you made?
 
Thanks.
 
Matthew
GeneralRe: Great Job!!membermilan.net5 Jun '07 - 1:01 
Compile to DLL and include it in your C# App
 
Milan Schoemig
Milan.Net

GeneralMinor problemmemberBeLHold24 Apr '07 - 8:49 
There is a minor problem. When my application minimizes to systemtray (user option) the I get a "Parameter is not valid" error message in the following procedure (on line OnHandleCreated(e)).
 
Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
MyBase.OnHandleCreated(e)
Me.AutoSize = False
End Sub
 
Otherwise, this is a great control.
Thank You
GeneralRe: Minor problemmembernogChoco24 Apr '07 - 18:43 
Hi BeLHold, thanks for your compliment and comment Smile | :)
 
I don't know why that line would cause a problem, as it's a standard call, but you can remove that sub altogether anyway. Originally, I inherited from 'Label' instead of 'Control', and overriding this sub was meant to counter the Label's default autosizing. I hope that will solve it, but as I don't know where the problem lies, I can't be sure.

NewsUpdate B offers significantly better performancemembernogChoco12 Apr '07 - 14:42 
Update B is now available for download - it's got some important improvements that should double the performance speed.
 
Instead of immediately calculating the item's height, the Update_VisibleLineNumberItems() method now builds a list of LineNumbers with their locations first, and computes the lineheights afterwards by comparing those locations. This halves the number of calls to the RTB's slow .GetPositionFromChar() method (which gets slower with each line added to its text), so the performance is doubled.
 
A timebased cutoff is now also being used during scrolling, to keep the computations from interfering with the scrollingspeed. There will still be at least one LineNumberItem visible, though, so when the document gets very big (multiple thousands of lines), the computation of that one item may start to cause a noticeable delay again (the RTB itself is responsible, though).
 
Get it while it's hot Cool | :cool:

GeneralRe: Update B offers significantly better performancememberKN112321 Apr '07 - 6:03 
Thanks!
Generalnice Thanks!!!memberKN112312 Apr '07 - 14:38 
Hey. Great job!
I will use this for my project, and give you credit. I've been looking something like this for few month last year and it has finally come!
 
Thanks,
Keehun
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 31 May 2007
Article Copyright 2007 by nogChoco
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid