ScintillaNET .net富文本框编辑插件 用法参考

1.Updating Dependent Controls

private void scintilla_UpdateUI(object sender, UpdateUIEventArgs e)
{
    if ((e.Change & UpdateChange.Selection) > 0)
    {
        // The caret/selection changed
        var currentPos = scintilla.CurrentPosition;
        var anchorPos = scintilla.AnchorPosition;
        toolStripStatusLabel.Text = "Ch: " + currentPos + " Sel: " + Math.Abs(anchorPos - currentPos);
    }
}

2.Basic Text Retrieval and Modification

it's usually best to identify the range of text you're interested in (through search, selection, or some other means) and use the GetTextRange method to copy only what you need into a string:

// Get the whole document
var wholeDoc = scintilla.Text;

// Get the first 256 characters of the document
var text = scintilla.GetTextRange(0, Math.Min(256, scintilla.TextLength));
Console.WriteLine(text);

// Sometimes it's nice to read the lines of a file to an array/list for processing
var myListOfItems = new List<string>();
foreach (var item in scintilla.Lines)
   myListOfItems.Add(item.Text);

// or...
List<string> myListOfItems = scintilla.Lines.Select(x => x.Text).ToList();

Insert, Append, and Delete

Modifications usually come in the form of insert, append, and delete operations. As was discussed above, using the Text property to make a small change in the document contents is highly inefficient. Instead try one of the following options:

scintilla.Text = "Hello";
scintilla.AppendText(" World"); // 'Hello' -> 'Hello World'
scintilla.DeleteRange(0, 5); // 'Hello World' -> ' World'
scintilla.InsertText(0, "Goodbye"); // ' World' -> 'Goodbye World'

// Remove line from Scintilla
scintilla.TargetStart = scintilla.Lines[0].Position;
scintilla.TargetEnd = scintilla.Lines[0].EndPosition;
scintilla.ReplaceTarget(string.Empty);

3.Selecting a Lexer

A language processor is referred to as a 'lexer' in Scintilla. Without going too much into parser theory, it's important to know that a lexer performs lexical analysis of a language, not syntatic analysis (parsing). In short, this means that the language support provided by Scintilla is enough to break the text into tokens and provide syntax highlighting, but not interpret what those tokens mean or whether they form an actual program. The distinction is important because developers using Scintilla often want to know how they can highlight incorrect code. Scintilla doesn't do that. If you want more than basic syntax highlighting, you'll need to couple Scintilla with a parser or even a background compiler.

To inform Scintilla what the current language is you must set the Lexer property to the appropriate enum value. In some cases multiple languages share the same Lexer enumeration value because these language share the same lexical grammar. For example, the Cpp lexer not only provides language support for C++, but all for C, C#, Java, JavaScript and others--because they are lexically similar. In our example we want to do C# syntax highlighting so we'll use the Cpp lexer.

scintilla.Lexer = Lexer.Cpp;

Defining Styles

The process of doing syntax highlighting in Scintilla is referred to as styling. When the text is styled, runs of text are assigned a numeric style definition in the Styles collection. For example, keywords may be assigned the style definition 1, while operators may be assigned the definition 2. It's entirely up to the lexer how this is done. Once done, however, you are then free to determine what style 1 or 2 look like. The lexer assigns the styles, but you define the style appearance. To make it easier to know which styles definitions a lexer will use, the Style object contains static constants that coincide with each Lexer enumeration value. For example, if we were using the Cpp lexer and wanted to set the style for single-line comments (//...) we would use the Style.Cpp.CommentLine constant to set the appropriate style in the Styles collection:

scintilla.Styles[Style.Cpp.CommentLine].Font = "Consolas";
scintilla.Styles[Style.Cpp.CommentLine].Size = 10;
scintilla.Styles[Style.Cpp.CommentLine].ForeColor = Color.FromArgb(0, 128, 0); // Green

To set the string style we would:

scintilla.Styles[Style.Cpp.String].Font = "Consolas";
scintilla.Styles[Style.Cpp.String].Size = 10;
scintilla.Styles[Style.Cpp.String].ForeColor = Color.FromArgb(163, 21, 21); // Red

To set the styles for number tokens we would do the same thing using the Style.Cpp.Number constant. For operators, we would use Style.Cpp.Operator, and so on.

If you use your imagination you will begin to see how doing this for each possible lexer token could be tedious. There is a lot of repetition. To reduce the amount of code you have to write Scintilla provides a way of setting a single style and then applying its appearance to every style in the collection. The general process is to:

  • Reset the Default style using StyleResetDefault.
  • Configure the Default style with all common properties.
  • Use the StyleClearAll method to apply the Default style to all styles.
  • Set any individual style properties

Using that time saving approach, we can set the appearance of our C# lexer styles like so:

// Configuring the default style with properties
// we have common to every lexer style saves time.
scintilla.StyleResetDefault();
scintilla.Styles[Style.Default].Font = "Consolas";
scintilla.Styles[Style.Default].Size = 10;
scintilla.StyleClearAll();

// Configure the CPP (C#) lexer styles
scintilla.Styles[Style.Cpp.Default].ForeColor = Color.Silver;
scintilla.Styles[Style.Cpp.Comment].ForeColor = Color.FromArgb(0, 128, 0); // Green
scintilla.Styles[Style.Cpp.CommentLine].ForeColor = Color.FromArgb(0, 128, 0); // Green
scintilla.Styles[Style.Cpp.CommentLineDoc].ForeColor = Color.FromArgb(128, 128, 128); // Gray
scintilla.Styles[Style.Cpp.Number].ForeColor = Color.Olive;
scintilla.Styles[Style.Cpp.Word].ForeColor = Color.Blue;
scintilla.Styles[Style.Cpp.Word2].ForeColor = Color.Blue;
scintilla.Styles[Style.Cpp.String].ForeColor = Color.FromArgb(163, 21, 21); // Red
scintilla.Styles[Style.Cpp.Character].ForeColor = Color.FromArgb(163, 21, 21); // Red
scintilla.Styles[Style.Cpp.Verbatim].ForeColor = Color.FromArgb(163, 21, 21); // Red
scintilla.Styles[Style.Cpp.StringEol].BackColor = Color.Pink;
scintilla.Styles[Style.Cpp.Operator].ForeColor = Color.Purple;
scintilla.Styles[Style.Cpp.Preprocessor].ForeColor = Color.Maroon;

Setting Keywords

The last thing we need to do to provide syntax highlighting is to inform the lexer what the language keywords and identifiers are. Since languages can often add keywords year after year, or because a lexer may sometimes be used for more than one language, it makes sense to make the keyword list configurable.

Since each Scintilla lexer is like a program until itself the number of keyword sets and the definition of each one vary from lexer to lexer. To determine what keyword sets a lexer supports you can call the DescribeKeywordSets method. This prints a human readable explanation of how many sets the current Lexer supports and what each means:

scintilla.Lexer = Lexer.Cpp;
Console.WriteLine(scintilla.DescribeKeywordSets());

// Outputs:
// Primary keywords and identifiers
// Secondary keywords and identifiers
// Documentation comment keywords
// Global classes and typedefs
// Preprocessor definitions
// Task marker and error marker keywords

Based on the output of DescribeKeywordSets I can determine that the first two sets are what I'm interested in for supporting general purpose C# syntax highlighting. To set a set of keywords you call the SetKeywords method. What 'primary' and 'secondary' means in the keyword set description is up to a bit of interpretation, but I'll break it down so that primary keywords are C# language keywords and secondary keywords are known .NET types. To set those I would call:

scintilla.SetKeywords(0, "abstract as base break case catch checked continue default delegate do else event explicit extern false finally fixed for foreach goto if implicit in interface internal is lock namespace new null object operator out override params private protected public readonly ref return sealed sizeof stackalloc switch this throw true try typeof unchecked unsafe using virtual while");
scintilla.SetKeywords(1, "bool byte char class const decimal double enum float int long sbyte short static string struct uint ulong ushort void");

NOTE: Keywords in a keyword set can be separated by any combination of whitespace (space, tab, '\r', '\n') characters.

Wondering where I got the list of keywords above? I typically pull them from the SciTE project .properties files -- a companion to Scintilla. The same could be done looking at the Notepad++ langs.model.xml file or any number of other text editors if you wanted to avoid going through the documentation for a language.

Complete Recipe

The complete recipe below will give you C# syntax highlighting using colors roughly equivalent to the Visual Studio defaults.

// Configuring the default style with properties
// we have common to every lexer style saves time.
scintilla.StyleResetDefault();
scintilla.Styles[Style.Default].Font = "Consolas";
scintilla.Styles[Style.Default].Size = 10;
scintilla.StyleClearAll();

// Configure the CPP (C#) lexer styles
scintilla.Styles[Style.Cpp.Default].ForeColor = Color.Silver;
scintilla.Styles[Style.Cpp.Comment].ForeColor = Color.FromArgb(0, 128, 0); // Green
scintilla.Styles[Style.Cpp.CommentLine].ForeColor = Color.FromArgb(0, 128, 0); // Green
scintilla.Styles[Style.Cpp.CommentLineDoc].ForeColor = Color.FromArgb(128, 128, 128); // Gray
scintilla.Styles[Style.Cpp.Number].ForeColor = Color.Olive;
scintilla.Styles[Style.Cpp.Word].ForeColor = Color.Blue;
scintilla.Styles[Style.Cpp.Word2].ForeColor = Color.Blue;
scintilla.Styles[Style.Cpp.String].ForeColor = Color.FromArgb(163, 21, 21); // Red
scintilla.Styles[Style.Cpp.Character].ForeColor = Color.FromArgb(163, 21, 21); // Red
scintilla.Styles[Style.Cpp.Verbatim].ForeColor = Color.FromArgb(163, 21, 21); // Red
scintilla.Styles[Style.Cpp.StringEol].BackColor = Color.Pink;
scintilla.Styles[Style.Cpp.Operator].ForeColor = Color.Purple;
scintilla.Styles[Style.Cpp.Preprocessor].ForeColor = Color.Maroon;
scintilla.Lexer = Lexer.Cpp;

// Set the keywords
scintilla.SetKeywords(0, "abstract as base break case catch checked continue default delegate do else event explicit extern false finally fixed for foreach goto if implicit in interface internal is lock namespace new null object operator out override params private protected public readonly ref return sealed sizeof stackalloc switch this throw true try typeof unchecked unsafe using virtual while");
scintilla.SetKeywords(1, "bool byte char class const decimal double enum float int long sbyte short static string struct uint ulong ushort void");

4.Displaying Line Numbers

scintilla.Margins[0].Width = 16;
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;
}

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值