Displaying Line Numbers - desjarlais/Scintilla.NET GitHub Wiki
Someone new to Scintilla might wonder why displaying line numbers gets its own recipe when one would assume it's as simple as flipping a single Boolean property. Well it's not. The subject of line numbers touches on the much larger subject of margins. In Scintilla there can be up to five margins (0 through 4) on the left edge of the control, of which, line numbers is just one of those. By convention Scintilla sets the Margin.Type
property of margin 0 to MarginType.Number
, making it the de facto line number margin. Any margin can display line numbers though if its Type
property is set to MarginType.Number
. Scintilla also hides line numbers by default by setting the Width
of margin 0 to zero. To display the default line number margin, increase its width:
scintilla.Margins[0].Width = 16;
You'll quickly find, however, that once you reach lines numbers in the 100's range the width of your line number margin is no longer sufficient. Scintilla doesn't automatically increase or decrease the width of a margin--including a line number margin. Why? It goes back to the fact that a margin could display line numbers or it could display something else entirely where dynamically growing and shrinking would be an undesirable trait.
The line number margin can be made to grow or shrink dynamically, it just requires a little extra code your part. In the recipe below we handle the TextChanged
event so we can know when the number of lines changes. (There are several other events we could use to determine the content has changed, but TextChanged
will do just fine.) Then we measure the width of the last line number in the document (or equivalent number of characters) using the TextWidth
method. Finally, set the Width
of the line number margin. Some caching of the calculation is thrown in for good measure since the number of lines will change far less than the TextChanged
event will fire.
private int maxLineNumberCharLength;
private void scintilla_TextChanged(object sender, EventArgs e)
{
// Did the number of characters in the line number display change?
// i.e. nnn VS nn, or nnnn VS nn, etc...
var maxLineNumberCharLength = scintilla.Lines.Count.ToString().Length;
if (maxLineNumberCharLength == this.maxLineNumberCharLength)
return;
// Calculate the width required to display the last line number
// and include some padding for good measure.
const int padding = 2;
scintilla.Margins[0].Width = scintilla.TextWidth(Style.LineNumber, new string('9', maxLineNumberCharLength + 1)) + padding;
this.maxLineNumberCharLength = maxLineNumberCharLength;
}
NOTE: The color of the text displayed in a line number margin can be controlled via the Style.LineNumber
style definition.
Custom Line Numbers
Scintilla has no built-in facilities for changing the line number display to some other text or format; however, using a standard text margin (rather than a number margin) we could simulate line numbers using any text or format we'd like.
In this example we'll assume our user is either a savant or masochist and wants line numbers displayed in hexadecimal format. The first step would be to change margin 0 (traditionally use for line numbers) to a right-aligned text margin.
scintilla.Margins[0].Type = MarginType.RightText;
scintilla.Margins[0].Width = 35;
Now we just need to set the margin text for each line to a hexadecimal representation of its line number and keep it up-to-date when the lines change. To monitor changes I'll use the Insert
and Delete
events because they include a property indicating the number of lines added or removed. Using the TextChanged
event wouldn't tell me that (easily). A small optimization I've also done is to update only the lines affected rather than every line. That would be anything following the changed line, but not before it--those are still valid.
private void UpdateLineNumbers(int startingAtLine)
{
// Starting at the specified line index, update each
// subsequent line margin text with a hex line number.
for (int i = startingAtLine; i < scintilla.Lines.Count; i++)
{
scintilla.Lines[i].MarginStyle = Style.LineNumber;
scintilla.Lines[i].MarginText = "0x" + i.ToString("X2");
}
}
private void scintilla_Insert(object sender, ModificationEventArgs e)
{
// Only update line numbers if the number of lines changed
if (e.LinesAdded != 0)
UpdateLineNumbers(scintilla.LineFromPosition(e.Position));
}
private void scintilla_Delete(object sender, ModificationEventArgs e)
{
// Only update line numbers if the number of lines changed
if (e.LinesAdded != 0)
UpdateLineNumbers(scintilla.LineFromPosition(e.Position));
}