RichTextBox syntax highlighting - c#

I am working with this code. It is for syntax highlighting in a RichTextBox. I am specifically looking at the function ProcessLine() and OnTextChanged(), which I have modified as such:
protected override void OnTextChanged(EventArgs e)
{
// Calculate stuff here.
m_nContentLength = this.TextLength;
int nCurrentSelectionStart = SelectionStart;
int nCurrentSelectionLength = SelectionLength;
m_bPaint = false;
// Find the start of the current line.
m_nLineStart = nCurrentSelectionStart;
while ((m_nLineStart > 0) && (Text[m_nLineStart - 1] != '\n'))
m_nLineStart--;
// Find the end of the current line.
m_nLineEnd = nCurrentSelectionStart;
while ((m_nLineEnd < Text.Length) && (Text[m_nLineEnd] != '\n'))
m_nLineEnd++;
// Calculate the length of the line.
m_nLineLength = m_nLineEnd - m_nLineStart;
// Get the current line.
m_strLine = Text.Substring(m_nLineStart, m_nLineLength);
// Process this line.
ProcessLine();
m_bPaint = true;
}
// Process a line.
private void ProcessLine()
{
// Save the position and make the whole line black
int nPosition = SelectionStart;
SelectionStart = m_nLineStart;
SelectionLength = m_nLineLength;
SelectionColor = Color.Black;
/*// Process the keywords
ProcessRegex(m_strKeywords, Settings.KeywordColor);
// Process numbers
if(Settings.EnableIntegers)
ProcessRegex("\\b(?:[0-9]*\\.)?[0-9]+\\b", Settings.IntegerColor);
// Process strings
if(Settings.EnableStrings)
ProcessRegex("\"[^\"\\\\\\r\\n]*(?:\\\\.[^\"\\\\\\r\\n]*)*\"", Settings.StringColor);
// Process comments
if(Settings.EnableComments && !string.IsNullOrEmpty(Settings.Comment))
ProcessRegex(Settings.Comment + ".*$", Settings.CommentColor);*/
SelectionStart = nPosition;
SelectionLength = 0;
SelectionColor = Color.Red;
m_nCurSelection = nPosition;
}
My first question is, when I enter into the ProcessLine() in OnTextChanged(), will I always have a newline character at the end of m_strLine? Will the smallest value or m_strLine be "\n" and the largest "any#ofchars+\n"?
And just so I have this right, SelectionStart is my caret position if SelectionLength is zero, and if SelectionLength is greater than zero my caret is at SelectStart+SelectionLength?
I am trying to modify this code to color a whole lot of different syntax expressions, and I plan to go through it one character at a time, for each line. How might this fair when pasting or loading a file of 20k+ lines?

All I can suggest you right now is to use something stable, more powerful and less error prone such as Scintilla for .NET and Color Code. These controls are free and open source. Try them out:
ScintillaNETColorCode - Syntax Highlighting/Colorization for .NET
RichTextBox is extremely inefficient for working with large text. Even if you get some decent highlighting, the performance issues will start to pop up pretty soon.

This is going to scale really badly. You should do what DelegateX suggests if your goal is simply a functioning application; if you're in this to learn how, start by figuring out ways to lower the amount of work that gets done. To that end, here are some general pointers:
Only highlighting text that's inside the window will be a massive improvement that doesn't have any visual side-effects - its probably also workable to break the text into blocks (by function, method, class, etc) and only highlight visible blocks, even the occluded portions, to avoid issues where an offset starting position affects the highlight. If you don't do this, you will run into situations where the first rendered line is partway through an if or parenthesis block, and you have an unbalanced syntax tree as a result.
You still won't be able to handle 20k lines with a RichTextBox control, but a few thousand should be speedy.

Facing the same issue and failed to find "5 minutes ready to go" solution, I have developed my own RichTextBox extension to highlight XML.
I developed it really quick because of time pressure and didn't have the time to revised it - so feel free to refine it.
Just copy & paste the extension code to use with your RichTextBox or copy the whole
application code including synchronous & asynchronous usage
Extension Method
// Use for asynchronous highlight
public delegate void VoidActionOnRichTextBox(RichTextBox richTextBox);
// Extension Class
public static class RichTextBoxExtensions
{
public static void HighlightXml(this RichTextBox richTextBox)
{
new StandardHighlight().HighlightXml(richTextBox);
}
public async static void HighlightXmlAsync(this RichTextBox richTextBox)
{
var helper = new StandardHighlight();
var win = new MainWindow();
await Task.Factory.StartNew(() =>
{
richTextBox.Dispatcher.BeginInvoke(new VoidActionOnRichTextBox(helper.HighlightXml), richTextBox);
});
}
}
// You can extent it with more highlight methods
public class StandardHighlight
{
public void HighlightXml(RichTextBox richTextBox)
{
// Collect Text-Box Information
var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text;
XDocument xDocument;
try
{
xDocument = XDocument.Parse(textRange);
}
catch
{
return;
}
var documentLines = xDocument.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
// Get the Longest Line Length
int? maxVal = null;
for (int i = 0; i < documentLines.Length; i++)
{
int thisNum = documentLines[i].Length;
if (!maxVal.HasValue || thisNum > maxVal.Value) { maxVal = thisNum; }
}
// Set Text-Box Width & Clear the Current Content
if (maxVal != null) richTextBox.Document.PageWidth = (double)maxVal * 5.5;
richTextBox.Document.Blocks.Clear();
#region *** Process Lines ***
foreach (var documentLine in documentLines)
{
// Parse XML Node Components
var indentSpace = Regex.Match(documentLine, #"\s+").Value;
var xmlTags = Regex.Matches(documentLine, #"(<[^/].+?)(?=[\s])|(<[^/].+?>)|(</.+?>)");
if (documentLine.Contains("<!--")) xmlTags = Regex.Matches(documentLine, #"(<[^/].+?>)"); // Parse comments
var nodeAttributes = Regex.Matches(documentLine, #"(?<=\s)(.+?)(?=\s)");
// Process XML Node
var nodeAttributesCollection = new List<Run>();
if (nodeAttributes.Count > 0)
{
for (int i = 0; i < nodeAttributes.Count; i++)
{
if (!(nodeAttributes[i].Value.Length < 2) && !(documentLine.Contains("<!--")))
{
var attributeName = $"{Regex.Match(nodeAttributes[i].Value, #"(.+?=)").Value}";
if (i == 0) attributeName = $" {Regex.Match(nodeAttributes[i].Value, #"(.+?=)").Value}";
var attributeValue = $"{Regex.Match(nodeAttributes[i].Value, #"(?<=(.+?=))"".+?""").Value} ";
if (i == nodeAttributes.Count - 1) attributeValue = attributeValue.Trim();
nodeAttributesCollection.Add(new Run { Foreground = new SolidColorBrush(Colors.Green), Text = $"{attributeName}" });
nodeAttributesCollection.Add(new Run { Foreground = new SolidColorBrush(Colors.Brown), Text = $"{attributeValue}" });
}
}
}
// Initialize IndentSpace
Run run = null;
if (indentSpace.Length > 1) run = new Run { Text = indentSpace };
// Initialize Open Tag
var tagText = xmlTags[0].Value.Substring(1, xmlTags[0].Value.Length - 2);
var tagTextBrush = new SolidColorBrush(Colors.Blue);
var tagBorderBruh = new SolidColorBrush(Colors.Red);
if (tagText.StartsWith("!--"))
{
tagTextBrush = new SolidColorBrush(Colors.DarkSlateGray);
tagBorderBruh = new SolidColorBrush(Colors.DarkSlateGray);
}
var openTag = new Run
{
Foreground = tagTextBrush,
Text = tagText
};
// Initialize Content Tag
var content = new Run
{
Foreground = new SolidColorBrush(Colors.Black),
};
// Initialize Paragraph
var paragraph = new Paragraph();
paragraph.Margin = new Thickness(0);
if (run != null) paragraph.Inlines.Add(run); // Add indent space if exist
// Process Open Tag
paragraph.Inlines.Add(new Run { Foreground = tagBorderBruh, Text = "<" });
paragraph.Inlines.Add(openTag);
// Process Open Tag Attributes
if (nodeAttributesCollection.Count > 0)
{
nodeAttributesCollection.ForEach(attribute => { paragraph.Inlines.Add(attribute); });
nodeAttributesCollection.Clear();
}
paragraph.Inlines.Add(new Run { Foreground = tagBorderBruh, Text = ">" });
// Process Closing Tag
if (xmlTags.Count > 1)
{
Run closingTag = new Run();
content.Text = documentLine.Replace(xmlTags[0].Value, "").Replace(xmlTags[1].Value, "").Trim();
closingTag = new Run
{
Foreground = new SolidColorBrush(Colors.Blue),
Text = xmlTags[1].Value.Substring(1, xmlTags[1].Value.Length - 2)
};
paragraph.Inlines.Add(content);
paragraph.Inlines.Add(new Run { Foreground = new SolidColorBrush(Colors.Red), Text = "<" });
paragraph.Inlines.Add(closingTag);
paragraph.Inlines.Add(new Run { Foreground = new SolidColorBrush(Colors.Red), Text = ">" });
}
richTextBox.Document.Blocks.Add(paragraph);
}
#endregion
}
}

Fixed version - with handling JSON as inner text and better elements extraction
public static void HighlightXml(this RichTextBox richTextBox)
{
// Collect Text-Box Information
var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text;
XmlDocument xmlDocument = new XmlDocument();
try
{
xmlDocument.LoadXml(textRange.Trim());
}
catch
{
return;
}
var documentLines = xmlDocument.OuterXml.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
// Get the Longest Line Length
int? maxVal = null;
for (int i = 0; i < documentLines.Length; i++)
{
int thisNum = documentLines[i].Length;
if (!maxVal.HasValue || thisNum > maxVal.Value) { maxVal = thisNum; }
}
// Set Text-Box Width & Clear the Current Content
if (maxVal != null) richTextBox.Document.PageWidth = (double)maxVal * 10;
richTextBox.Document.Blocks.Clear();
#region *** Process Lines ***
foreach (var documentLine in documentLines)
{
// Parse XML Node Components
var indentSpace = Regex.Match(documentLine, #"\s+").Value;
var xmlTags = Regex.Matches(documentLine, #"(?<=<)[^>\s+]*");
if (documentLine.Contains("<!--")) xmlTags = Regex.Matches(documentLine, #"(<[^/].+?>)");
var nodeAttributes = Regex.Matches(documentLine, #"(?<=\s)[^><:\s]*=*(?=[>,\s])");
// Process XML Node
var nodeAttributesCollection = new List<Run>();
if (nodeAttributes.Count > 0)
{
for (int i = 0; i < nodeAttributes.Count; i++)
{
if (!(nodeAttributes[i].Value.Length < 2) && !(documentLine.Contains("<!--")))
{
var attributeName = $"{Regex.Match(nodeAttributes[i].Value, #"(.+?=)").Value}";
if (i == 0) attributeName = $" {Regex.Match(nodeAttributes[i].Value, #"(.+?=)").Value}";
var attributeValue = $"{Regex.Match(nodeAttributes[i].Value, #"(?<=(.+?=))"".+?""").Value} ";
if (i == nodeAttributes.Count - 1) attributeValue = attributeValue.Trim();
nodeAttributesCollection.Add(new Run { Foreground = new SolidColorBrush(Colors.Green), Text = $"{attributeName}" });
nodeAttributesCollection.Add(new Run { Foreground = new SolidColorBrush(Colors.Brown), Text = $"{attributeValue}" });
}
}
}
// Initialize IndentSpace
Run run = null;
if (indentSpace.Length > 1) run = new Run { Text = indentSpace };
// Initialize Open Tag
var tagText = xmlTags[0].Value;//.Substring(1, xmlTags[0].Value.Length - 2);
var tagTextBrush = new SolidColorBrush(Colors.Blue);
var tagBorderBruh = new SolidColorBrush(Colors.Red);
if (tagText.StartsWith("!--"))
{
tagTextBrush = new SolidColorBrush(Colors.DarkSlateGray);
tagBorderBruh = new SolidColorBrush(Colors.DarkSlateGray);
}
var openTag = new Run
{
Foreground = tagTextBrush,
Text = tagText
};
// Initialize Content Tag
var content = new Run
{
Foreground = new SolidColorBrush(Colors.Black),
};
// Initialize Paragraph
var paragraph = new Paragraph();
paragraph.Margin = new Thickness(0);
if (run != null) paragraph.Inlines.Add(run); // Add indent space if exist
// Process Open Tag
paragraph.Inlines.Add(new Run { Foreground = tagBorderBruh, Text = "<" });
paragraph.Inlines.Add(openTag);
// Process Open Tag Attributes
if (nodeAttributesCollection.Count > 0)
{
nodeAttributesCollection.ForEach(attribute => { paragraph.Inlines.Add(attribute); });
nodeAttributesCollection.Clear();
}
paragraph.Inlines.Add(new Run { Foreground = tagBorderBruh, Text = ">" });
// Process Closing Tag
if (xmlTags.Count > 1)
{
Run closingTag = new Run();
content.Text = documentLine.Replace($"<{xmlTags[0].Value}>", "").Replace($"<{xmlTags[1].Value}>", "").Trim();
closingTag = new Run
{
Foreground = new SolidColorBrush(Colors.Blue),
Text = xmlTags[1].Value.Substring(1, xmlTags[1].Value.Length - 1)
};
paragraph.Inlines.Add(content);
paragraph.Inlines.Add(new Run { Foreground = new SolidColorBrush(Colors.Red), Text = "<" });
paragraph.Inlines.Add(closingTag);
paragraph.Inlines.Add(new Run { Foreground = new SolidColorBrush(Colors.Red), Text = ">" });
}
richTextBox.Document.Blocks.Add(paragraph);
}
#endregion
}

Related

WPF window won't appear when using Show or ShowDialog

I'm a junior programmer.
A window of my WPF program won't show itself anymore. I can't seem to find out why.
My program has three different windows. When the first one shows up and I click on the button to start the rest of the program, it does run, it also goes through the code of the window that I want to see, but it does not show anything.
I hope that any of you can help.
Below are parts of my code. The first one should already call the window
private void SortingPartOne(){
Function rename = new Function();
rename.wrkLocTree.CurrentDirectory = StartingPath;
rename.wrkLocTree.GiveUpdatePerFolder();
rename.wrkLocTree.Show();
The second part creates a new class and also should 'create' the window.
public class Function{
public WorkLocationTree wrkLocTree = new();
The third part is for updating the window that won't show up
public void GiveUpdatePerFolder(){
//variable declaration
int dirCounter;
string directoryToAdd = Path.GetFileName(CurrentDirectory);
if(String.IsNullOrEmpty(BeginPath)) BeginPath = MainWindow.StartingPath;
string dirs = Path.GetFileName(BeginPath) + CurrentDirectory.Replace(BeginPath, "");
List<string> directories = dirs.Split('\\').ToList();
dirCounter = directories.Count-1;
// End variable declaration */
if(!oldDirectories.Any()){
if(_textBoxes == null || _textBoxes[0] == null) _textBoxes = new TextBox[1];
_textBoxes[0] = new TextBox{ Text = directoryToAdd, Width = 250 };
AddTextBox(0);
}
else{
if(directories == oldDirectories) return;
if(directories.Count > oldDirectories.Count){
if(_textBoxes == null || _textBoxes[0] == null) _textBoxes = new TextBox[1];
else{
List<TextBox> txtBxConverter = _textBoxes.ToList();
txtBxConverter.Add(new TextBox());
txtBxConverter.Add(new TextBox());
_textBoxes = txtBxConverter.ToArray();
}
_textBoxes[dirCounter] = new TextBox{ Text = directoryToAdd, Width = 100 };
if(_lines == null || _lines[0] == null) _lines = new Line[2];
else{
List<Line> converter = _lines.ToList();
converter.Add(new Line());
converter.Add(new Line());
_lines = converter.ToArray();
}
int lineCounter = _lines.Length > 0 ? _lines.Length : 0;
_lines[lineCounter] = new Line{ VerticalAlignment = VerticalAlignment.Center, X1 = 0, X2 = 7.5, Stroke = Brushes.Chocolate, StrokeThickness = 3 };
_lines[lineCounter + 1] = new Line{ VerticalAlignment = VerticalAlignment.Center, Y1 = -14, Y2 = 1.5, Stroke = Brushes.Chocolate, StrokeThickness = 3 };
AddLine(dirCounter, lineCounter);
AddTextBox(dirCounter);
}
else if(directories.Count < oldDirectories.Count){
ClearLayer(dirCounter, 2);
_textBoxes[directories.Count - 1].Text = directoryToAdd;
AddTextBox(dirCounter);
}
else if(directories.Count == oldDirectories.Count){
if(directories[directories.Count-1] == oldDirectories[directories.Count-1]){
return;
}
ClearLayer(dirCounter, 1);
_textBoxes[dirCounter].Text = directoryToAdd;
AddTextBox(dirCounter);
}
}
oldDirectories.RemoveRange(0, oldDirectories.Count);
for(int i = 0; i < directories.Count; i++){
oldDirectories.Add(directories[i]);
}
InitializeComponent();
}
There's also a small part where the textboxes get added to the Stack panels. But I don't think that it would be necessary to share

C# Form/Control Width Not Updating All the Time

I've made a small tool bar that sits in a transparent form, it loads a variable sized menu from a text file and can be changed on the fly. Each button is a type of Label, the bar is just a list of buttons and adds/removes them in the correct spots. Width of the form is only a little bigger than the menu bar so that sub menu isn't cut off
Everything is working sweet except, when I reload everything part of the toolbar is lost. I've attempted to change the width so many ways, I've cleared and removed the controls from the form, refreshing the form/menu, updating it etc however nothing seems to make it work as intended EXCEPT if I call the reload function twice in a row, it works. I can't see why calling it once doesn't work but calling it twice works.
I'm fine with calling reload twice in a row as it would only be called a couple times a week.
Question: what on earth is causing this?
photo of issues first photo shows what it should look like, second is after removing a menu button and reloading, third is after adding a button and reloading
//calling this.reload() doesn't work
//calling this.reload();this.reload() works
void reload(Object o = null, EventArgs e = null)
{
this._menuBar.clear();
this.loadFromFile();
}
void loadFromFile(Object o = null, EventArgs e = null)
{
try
{
if (File.Exists("kpi.txt"))
{
string cline = "", cmenu = "", lhs = "";
menuList mb = null;
StreamReader sr = new StreamReader("kpi.txt");
while (!sr.EndOfStream)
{
cline = sr.ReadLine(); //get current line
if (cline.Length > 0 && cline[0] != ';')
{
//check if main menu/command
if (cline[0] == '[')
{
cmenu = Regex.Match(cline, #"(?<=^\[)[a-zA-Z -\#_{-~\^\r\n]+(?=\])").Value;
if (cmenu != "")
{
mb = this._menuBar.addMenuButton(cmenu);
mb.data["options"] = Regex.Match(cline, #"\/\w+$").Value;
var match = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
mb.data["count"] = (match.Success ? match.Value : "0");
mb.data["copy"] = "";
applyMenuOptions(mb, false);
}
}
//just a standard line
else
{
cline = cline.Trim();
lhs = Regex.Match(cline, #"^[^\;\<\[\]\r\n]+(?=$|\<|\;)").Value;
if (mb.getSubMenuItem(lhs) == null)
{
var newButton = mb.addSubMenu(lhs);
if (newButton != null)
{
newButton.parent = mb;
newButton.data["options"] = mb.data["options"];
newButton.data["copy"] = Regex.Match(cline, #"((?<=\;)[^\[\]\<\r\n]+(?=<|$))").Value;
var matches = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
int intout = 0;
if (int.TryParse(matches.Value, out intout))
{//no description
newButton.data["description"] = "";
newButton.data["count"] = intout.ToString();
}
else
{
newButton.data["description"] = matches.Value;
newButton.data["count"] = (matches.NextMatch().Success ? matches.NextMatch().Value : "0");
}
applyMenuOptions(newButton);
newButton.addMiddleClick(this.addcopy);
if (newButton.data["options"].Contains("i"))
{
newButton.addRightClick(this.appendInfo);
newButton.addRightClick(this.increment);
}
}
}
}
}
}
sr.Close();
this._menuBar.squish();
this.Width = this._menuBar.Width+50;
}
else
{
menuList mb = this._menuBar.addMenuButton("menu");
mb.data["options"] = "\\m";
mb.data["count"] = "0";
mb.data["copy"] = "";
mb.data["description"] = "";
applyMenuOptions(mb, false);
saveDictonary();
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to load data " + ex);
//ILog log = LogManager.GetLogger(typeof(Program));
//log.Info(ex);
}
}
public menuList addMenuButton(string s, int w = 0, int h = 0, int x = -1, int y = -1)
{
menuList mb = new menuList(this._form, s);
if (this.menuItems.Exists(z => z.Text == s)) return null;
mb.Width = (w==0?settings.intOf("ButtonWidth"):w);
mb.Height = (h==0?settings.IntOf("ButtonHeight"):h);
if (x == -1 || y == -1)
mb.Location = new Point(this.menuItems.Count > 0 ? this.menuItems.Last().Location.X + this.menuItems.Last().Width : padding);
else mb.Location = new Point(x, y);
mb.BringToFront();
mb.Show();
this.menuItems.Add(mb);
// this.Refresh();
return mb;
}
internal void clear()
{
foreach(var i in this.menuItems)
{
this._form.Controls.Remove(i);
i.clear();
i.Dispose();
}
this.menuItems.Clear();
this._form.Controls.Remove(this);
this.menuItems = new List<menuList>();
this._form.Controls.Add(this);
}
internal void squish()
{
try
{
this.Width = (this.menuItems.Count * this.menuItems.First().Width) + (2 * padding);
}
catch(Exception ex) { MessageBox.Show(""+ex); }
}
Found the culprit, bother the button class and the tool bar class were both adding themselves to the form control instead of button class adding to the tool bar (picture box) controls!
Removing transparency showed the buttons not moving when the tool bar was moved!

LINQ instead of for loop in wpf?

I am working with the WPF RichTextbox. I have to get all the lines in the RichTextbox. So I am using a for loop to get all the lines, but the RichTextbox contains a large text content. It takes too much time.
So how do I get around a 1000 line loop in less time?
I've tried parallel.for but it gives an exception as it tries to get each line of the RichTextbox text.
Here is my code.
for (Int32 icnt = 0; icnt <= iLineCount; icnt++)
{
LineDetails lnDtls = new LineDetails();
lnDtls.LineText = GetLineText(txtAppendValue.CaretPosition.GetLineStartPosition(icnt));
iCurrentEnd = iCurrentEnd + lnDtls.LineText.Length;
lnDtls.LineLength = iCurrentEnd;
listLines.Add(lnDtls);
}
GetLineText():
String GetLineText(TextPointer TextPointer)
{
tp1 = TextPointer.GetLineStartPosition(0);
if (tp1 == null)
{
return null;
}
else
{
tpNextLine2 = tp1.GetLineStartPosition(1);
if (tr != null)
{
tr = null;
}
if (tpNextLine2 == null)
{
tpNextLine2 = txtAppendValue.Document.ContentEnd;
}
tr = new TextRange(tp1, tpNextLine2);
return tr.Text;
}
}
So can I use LINQ instead of for loop for fast execution?
I don't see why it needs to be this complicated. A few simple line of code would give you all the lines in a rich text box.
string text = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text;
text.Replace("\r\n", "\n");
var lines = text.Split(new char[] {'\n'});

AvalonEdit: Getting Syntax Highlighted Text

I'm registering syntax highlighting with AvalonEdit with:
PythonPrompt.SyntaxHighlighting = pythonHighlighting;
Text can then be input by the user throughout the course of the program. Is there a way to take the formatted text and move it to a TextBlock without loosing the formatting?
As this formatted text will not be edited again I presume it is more efficient to create a TextBlock rather than creating a TextEditor on the fly.
I managed to get something that works. Its based off the latest code for AvalonEdit (HighlightedLine and RichTextModel)
TextBlock Item = new TextBlock();
Code = Code.Replace("\t", new String(' ', Editor.Options.IndentationSize));
TextDocument Document = new TextDocument(Code);
IHighlightingDefinition HighlightDefinition = Editor.SyntaxHighlighting;
IHighlighter Highlighter = new DocumentHighlighter(Document, HighlightDefinition.MainRuleSet);
int LineCount = Document.LineCount;
for (int LineNumber = 1; LineNumber <= Document.LineCount; LineNumber++)
{
HighlightedLine Line = Highlighter.HighlightLine(LineNumber);
string LineText = Document.GetText(Line.DocumentLine);
int Offset = Line.DocumentLine.Offset;
int SectionCount = Line.Sections.Count;
for (int SectionNumber = 0; SectionNumber < SectionCount; SectionNumber++)
{
HighlightedSection Section = Line.Sections[SectionNumber];
//Deal with previous text
if (Section.Offset > Offset)
{
Item.Inlines.Add(
new Run(Document.GetText(Offset, Section.Offset - Offset))
);
}
Run RunItem = new Run(Document.GetText(Section));
if (RunItem.Foreground != null)
{
RunItem.Foreground = Section.Color.Foreground.GetBrush(null);
}
if (Section.Color.FontWeight != null)
{
RunItem.FontWeight = Section.Color.FontWeight.Value;
}
Item.Inlines.Add(RunItem);
Offset = Section.Offset + Section.Length;
}
//Deal with stuff at end of line
int LineEnd = Line.DocumentLine.Offset + LineText.Length;
if (LineEnd > Offset)
{
Item.Inlines.Add(
new Run(Document.GetText(Offset, LineEnd-Offset))
);
}
//If not last line add a new line
if (LineNumber < LineCount)
{
Item.Inlines.Add(new Run("\n"));
}
}

Avoid Scanning EntireRichtextbox Highlighting [duplicate]

This question already has answers here:
RichTextBox syntax highlighting in real time--Disabling the repaint
(3 answers)
Closed 9 years ago.
im working on a code editor and i came up with this set of codes:
public class Test2 : Form {
RichTextBox m_rtb = null;
public static void Main() {
Application.Run(new Test2());
}
public Test2() {
Text = "Test2";
ClientSize = new Size(400, 400);
m_rtb = new RichTextBox();
m_rtb.Multiline = true;
m_rtb.WordWrap = false;
m_rtb.AcceptsTab = true;
m_rtb.ScrollBars = RichTextBoxScrollBars.ForcedBoth;
m_rtb.Dock = DockStyle.Fill;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
m_rtb.SelectionColor = Color.Black;
Controls.Add(m_rtb);
Parse();
m_rtb.TextChanged += new EventHandler(this.TextChangedEvent);
}
void Parse() {
String inputLanguage =
"// Comment.\n" +
"using System;\n" + "\n" +
"public class Stuff : Form { \n" +
" public static void Main(String args) {\n" +
" }\n" +
"}\n" ;
// Foreach line in input,
// identify key words and format them when adding to the rich text box.
Regex r = new Regex("\\n");
String [] lines = r.Split(inputLanguage);
foreach (string l in lines) {
ParseLine(l);
}
}
void ParseLine(string line) {
Regex r = new Regex("([ \\t{}();])");
String [] tokens = r.Split(line);
foreach (string token in tokens) {
// Set the token's default color and font.
m_rtb.SelectionColor = Color.Black;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
// Check for a comment.
if (token == "//" || token.StartsWith("//")) {
// Find the start of the comment and then extract the whole comment.
int index = line.IndexOf("//");
string comment = line.Substring(index, line.Length - index);
m_rtb.SelectionColor = Color.LightGreen;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
m_rtb.SelectedText = comment;
break;
}
// Check whether the token is a keyword.
String [] keywords = { "public", "void", "using", "static", "class" };
for (int i = 0; i < keywords.Length; i++) {
if (keywords[i] == token) {
// Apply alternative color and font to highlight keyword.
m_rtb.SelectionColor = Color.Blue;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Bold);
break;
}
}
m_rtb.SelectedText = token;
}
m_rtb.SelectedText = "\n";
}
private void TextChangedEvent(object sender, EventArgs e) {
// Calculate the starting position of the current line.
int start = 0, end = 0;
for (start = m_rtb.SelectionStart - 1; start > 0; start--) {
if (m_rtb.Text[start] == '\n') { start++; break; }
}
// Calculate the end position of the current line.
for (end = m_rtb.SelectionStart; end < m_rtb.Text.Length; end++) {
if (m_rtb.Text[end] == '\n') break;
}
// Extract the current line that is being edited.
String line = m_rtb.Text.Substring(start, end - start);
// Backup the users current selection point.
int selectionStart = m_rtb.SelectionStart;
int selectionLength = m_rtb.SelectionLength;
// Split the line into tokens.
Regex r = new Regex("([ \\t{}();])");
string [] tokens = r.Split(line);
int index = start;
foreach (string token in tokens) {
// Set the token's default color and font.
m_rtb.SelectionStart = index;
m_rtb.SelectionLength = token.Length;
m_rtb.SelectionColor = Color.Black;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
// Check for a comment.
if (token == "//" || token.StartsWith("//")) {
// Find the start of the comment and then extract the whole comment.
int length = line.Length - (index - start);
string commentText = m_rtb.Text.Substring(index, length);
m_rtb.SelectionStart = index;
m_rtb.SelectionLength = length;
m_rtb.SelectionColor = Color.LightGreen;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
break;
}
// Check whether the token is a keyword.
String [] keywords = { "public", "void", "using", "static", "class" };
for (int i = 0; i < keywords.Length; i++) {
if (keywords[i] == token) {
// Apply alternative color and font to highlight keyword.
m_rtb.SelectionColor = Color.Blue;
m_rtb.SelectionFont = new Font("Courier New", 10, FontStyle.Bold);
break;
}
}
index += token.Length;
}
// Restore the users current selection point.
m_rtb.SelectionStart = selectionStart;
m_rtb.SelectionLength = selectionLength;
}
}
problem was everytime i press space keys or type the entire code editor keeps on scanning like searching on what to highlight and i find it a bit annoying ...
so i just want to ask for possible solution about this ... to avoid highlighting of whole richtextbox like scanning what to highlight next .
thanks a lot in advance for the help! more power!
I answered this in your most recent question, but in case someone else is reading this and doesn't find it, I'll post it here (Since this is specifically about performance):
You can use a couple of things to improve performance:
1) You can get the line that the user is editing by getting the text range from the current selection. I would recommend using WPF's richtextbox as it contains much more features and has the useful TextPointer class which you can utilise to get the current line. It seems like you are using WinForms however, so this can be done with these few lines of code (with the addition of some trivial code):
int start_index = RTB.GetFirstCharIndexOfCurrentLine();
int line = RTB.GetLineFromCharIndex(index);
int last_index = RTB.GetFirstCharIndexFromLine(line+1);
RTB.Select(start_index, last_index);
You can then work with the current selection.
2) If you don't want it to update so frequently, you can create a timer which measures the delay since the last edit, and reset the timer if another edit is made before the timer elapses.

Categories

Resources