In C# WPF, I have a function to retrieve text from flowdocumentreader:
static string GetText(TextPointer textStart, TextPointer textEnd)
{
StringBuilder output = new StringBuilder();
TextPointer tp = textStart;
while (tp != null && tp.CompareTo(textEnd) < 0)
{
if (tp.GetPointerContext(LogicalDirection.Forward) ==
TextPointerContext.Text)
{
output.Append(tp.GetTextInRun(LogicalDirection.Forward));
}
tp = tp.GetNextContextPosition(LogicalDirection.Forward);
}
return output.ToString();
}
Then I use the function as follow:
string test = GetText(rtb.Document.ContentStart, rtb.Document.ContentEnd);
However, the string "test" ignores all the line breaks, which means "\r\n". It does keep the tab character, "\t".
My question is how to keep all the line breaks? I want to automatically highlight the first sentence of each paragraph, so I need to detect the line break characters, "\r\n".
Thanks in advance for your time.
Update:
I load the .rtf document into flowdocumentreader like this:
if (dlg.FileName.LastIndexOf(".rtf") != -1)
{
paraBodyText.Inlines.Clear();
string temp = File.ReadAllText(dlg.FileName, Encoding.UTF8);
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(temp));
TextRange textRange = new TextRange(flow.ContentStart, flow.ContentEnd);
textRange.Load(stream, DataFormats.Rtf);
myDocumentReader.Document = flow;
stream.Close();
}
Edited
assuming that each paragraph has at least one sentence that ends with a dot, you can use the following code to make the first sentence bold:
List<TextRange> ranges = new List<TextRange>();
foreach (Paragraph p in rtb.Document.Blocks.OfType<Paragraph>())
{
TextPointer pointer = null;
foreach (Run r in p.Inlines.OfType<Run>())
{
int index = r.Text.IndexOf(".");
if (index != -1)
{
pointer = r.ContentStart.GetPositionAtOffset(index);
}
}
if (pointer == null)
continue;
var firsSentence = new TextRange(p.ContentStart, pointer);
ranges.Add(firsSentence);
}
foreach (var r in ranges)
{
r.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
}
Hey Im creating my own coding language and I already have the entire application set up with save, open, close, new, etc.
In order for the "Run" part to work, I need a way to scan and test every single line in richTextBox1.
Maybe for past Java users, something along the lines of the "java-util-scanner," but easier to use for testing each line.
Does anyone know a way to do this, where a string "scannedString" would be tested as so:
if(scannedString == "#load(moduleFiles)") {
//action here
}
string scannedStringNextLine = ???
if(scannedStringNextLine = "") {
//action here
}
Eventually it would look more like this:
if(scannedString == "code1" || scannedString == "code2" etc... ) {
if(scannedString == "code1") {
//action here
}
} else {
//error message
}
hope this is enough information...
To get lines of code of the RichTextBox you can split the content by a new line symbol:
var lines = this.richTextBox.Text.Split('\n').ToList();
The lines are in order of appearance. Then you can go through lines in a for or foreach loop:
foreach (var line in lines)
{
// access a line, evaluate it, etc.
}
One way to do it is to split the text on the newline charaters and then parse each line for the text you care about:
private void btnScan_Click(object sender, EventArgs e)
{
var code = richTextBox1.Text;
foreach (var line in code.Split(new []{'\n','\r'},
StringSplitOptions.RemoveEmptyEntries))
{
CheckForLoad(line);
}
}
void CheckForLoad(string line)
{
if (string.IsNullOrWhiteSpace(line)) return;
int i = line.IndexOf("#load");
if (i < 0) return;
int openParen = line.IndexOf("(", i + 1);
if (openParen < 0) return;
int closeParen = line.IndexOf(")", openParen + 1);
if (closeParen < 0) return;
string modules = line.Substring(openParen + 1, closeParen - openParen - 1);
MessageBox.Show(string.Format("Loading modules: {0}", modules));
}
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"));
}
}
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
}
How do you get the Notes text from the current PowerPoint slide using C#?
I believe this might be what you are looking for:
string s = slide.NotesPage.Shapes[2].TextFrame.TextRange.Text
slide.NotesPage.Shapes[2].TextFrame.TextRange.Text = "Hello World"
Here is my code that I use for getting the slide notes. Still developing it, but seems to do the trick for the time being. Even in my simple test PPT the slide notes are not always the [2] element in the shapes array, so it is important to check.
private string GetNotes(Slide slide)
{
if (slide.HasNotesPage == MsoTriState.msoFalse)
return string.Empty;
string slideNodes = string.Empty;
var notesPage = slide.NotesPage;
int length = 0;
foreach (Shape shape in notesPage.Shapes)
{
if (shape.Type == MsoShapeType.msoPlaceholder)
{
var tf = shape.TextFrame;
try
{
//Some TextFrames do not have a range
var range = tf.TextRange;
if (range.Length > length)
{ //Some have a digit in the text,
//so find the longest text item and return that
slideNodes = range.Text;
length = range.Length;
}
Marshal.ReleaseComObject(range);
}
catch (Exception)
{}
finally
{ //Ensure clear up
Marshal.ReleaseComObject(tf);
}
}
Marshal.ReleaseComObject(shape);
}
return slideNodes;
}