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"));
}
}
Related
Ive been working at this for hours and cant seem to figure out how to correctly display the data in a table
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int pointX = 30;
int pointY = 40;
while (!csvParser.EndOfData) {
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
int index = 0;
for(index = 0; index < rowNums;index++) {
string Name = fields[index];
TextBox n = new TextBox();
n.Text = Name;
n.Location = new Point(pointX, pointY);
panel2.Controls.Add(n);
panel2.Show();
pointY += 20;
if(index != 0) {
pointX += 100;
}
}
}
}
Whats happening so far is im grabbing a csv file stored in the path variable and reading it the output is accessible through fields[] This works fine then I am trying to create textbox to put the data into based on rows however what i currently have comes out look like this
Program Display
I would like to display the column names and rows correctly in order here is an example image of what it looks like in notepad
Notepad Display
In notepad you will see each new line is a row and every , dictates a new entry in the row and i wanna display it this way in my program but in textbox
Also note that not all csv files that this program will be opening are short most will be large files with thousands or rows or more so theres no way that it could be simply putting fields[0] hard coded
You are much better off using a DataGridView to display this type of data in a table format.
From the toolbox add the DataGridView control to your form. You will need to build a DataTable that will bind to your DataGridview.
Below is what you can use(I commented out where you are skipping the header in your CSV file, and am using that line to get the column headers to be used in the datagrid)
var dt = new DataTable();
var lineNo = 0;
using (var csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
//csvParser.ReadLine();
while (!csvParser.EndOfData)
{
var fields = csvParser.ReadFields();
var rowNums = fields.Length;
var row = dt.NewRow();
lineNo += 1;
int index = 0;
for (index = 0; index < rowNums; index++)
{
if (lineNo==1)
{
dt.Columns.Add(fields[index]);
}
else
{
row[index] = fields[index];
}
}
if (lineNo == 1) continue;
dt.Rows.Add(row);
dt.AcceptChanges();
}
}
dataGridView1.DataSource = dt;
I think the controls are overlapping, so you can not see them. That they are overlapping like chairs is the problem. You are not resetting your coordinates.
Here an improvement:
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int offsetX = 30;
int offsetY = 40;
int counter;
while (!csvParser.EndOfData) {
int pointerY = ++counter * offsetY; // first counter increments by one, then counter times offsetY occurs
int pointerX;
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
for(int index = 0; index < rowNums;index++) {
pointerX = (index + 1) * offsetX;
string name = fields[index];
TextBox n = new TextBox() { Text = name, Location = new Point(pointerX, pointerY) };
panel2.Controls.Add(n);
panel2.Show(); // should be unnecessary
}
}
}
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'});
Is there a way to remove sections of a document where i can specify the beginning and ending tags?
i need a way that i can remove a section of the document by passing in both my start and end catches, (##DELETEBEGIN and ##DELETEEND)
for example i have this in my document:
Hello, welcome to this document
##DELETEBEGIN{Some values to check in the code}
Some text that will be removed if the value is true
##DELETEEND
Final Line
If you need to delete text from ##DELETEBEGIN to ##DELETEEND, where ##DELETEBEGIN is not at the beginning of a Paragraph and ##DELETEEND is not at the end of a Paragraph, this code should work.
DocX document = DocX.Load("C:\\Users\\phil\\Desktop\\text.docx");
bool flag = false;
List<List<string>> list1 = new List<List<string>>();
List<string> list2 = new List<string>();
foreach (Novacode.Paragraph item in document.Paragraphs)
{
//use this if you need whole text of a paragraph
string paraText = item.Text;
var result = paraText.Split(' ');
int count = 0;
list2 = new List<string>();
//use this if you need word by word
foreach (var data in result)
{
string word = data.ToString();
if (word.Contains("##DELETEBEGIN")) flag = true;
if (word.Contains("##DELETEEND"))
{
flag = false;
list2.Add(word);
}
if (flag) list2.Add(word);
count++;
}
list1.Add(list2);
}
for (int i = 0; i < list1.Count(); i++)
{
string temp = "";
for (int y = 0; y < list1[i].Count(); y++)
{
if (y == 0)
{
temp = list1[i][y];
continue;
}
temp += " " + list1[i][y];
}
if (!temp.Equals("")) document.ReplaceText(temp, "");
}
document.Save();
I have to give some credit to this post for looping through each word.
I think i have found a solution to this, at least it works for me, please let me know if there is anything i can do better:
the deleteCommand would be the ##DELETEBEGIN string and the deleteEndCommand would be the ##DELETEEND
private void RemoveSection(DocX doc, string deleteCommand, string deleteEndCommand)
{
try
{
int deleteStart = 0;
int deleteEnd = 0;
//Get the array of the paragraphs containing the start and end catches
for (int i = 0; i < doc.Paragraphs.Count; i++)
{
if (doc.Paragraphs[i].Text.Contains(deleteCommand))
deleteStart = i;
if (doc.Paragraphs[i].Text.Contains(deleteEndCommand))
deleteEnd = i;
}
if (deleteStart > 0 && deleteEnd > 0)
{
//delete from the paraIndex as the arrays will shift when a paragraph is deleted
int paraIndex = deleteStart;
for (int i = deleteStart; i <= deleteEnd; i++)
{
doc.RemoveParagraphAt(paraIndex);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
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.
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
}