c# rtb: Bolden inline overrides another bolden inline - c#

My projekt is a Windows 10 universal app!
I almost have for two month the problem, that when I bold two words in one rtb, the second overrides the first bold inline.
Example:
I want to bold:
Hello; Bye
Text from rtb:
Hello and Bye
Now I search with regex, weather ther is "Hello"/ "Bye" in rtb.
For each time there is "Hello" in rtb I insert a bolden inline with the text "Hello" in the same Position than "hello" stand befor.
After that I make the same with "Bye".
My Code:
string text = run.Text; -> "Hello and Bye"
MatchCollection mc = Regex.Matches(text, "Hello", RegexOptions.Multiline);
int i = 0;
var bold = new Bold();
int iIn = 0;
int iLe = 0;
p.Inlines.Clear(); -> p = Paragraph from rtb
foreach (Match match in mc)
{
p.Inlines.Add(new Run { Text = text.Substring(i, match.Index - i) });
bold.Inlines.Add(new Run { Text = text.Substring(match.Index, match.Length) });
p.Inlines.Add(bold);
i = match.Index + match.Length;
if (i < text.Length)
{
p.Inlines.Add(new Run { Text = text.Substring(i) });
}
}
This is followed by the same Code with bye.
Now the Problem is, that I clear the first bold inline (Hello) while insert the second bold inline (Bye).
Does anyone know an alternative to bold an specific word in a rtb or a Suggestion to improve the Code? I almost tryed everything but nothing really worked...

Use the following in order to select the relevant characters to make bold:"
public void Select(
TextPointer start,
TextPointer end
)
In order to get TextPointer, try this (not checked):
TextPointer pointer = document.ContentStart;
TextPointer start = pointer.GetPositionAtOffset(0);
TextPointer end = start.GetPositionAtOffset(5);
rtb.Select (start,end); // for example to select Hello
// Then change the font to bold

Related

How to highlight text in a sentence using OpenXML?

I am using below code to search and highlight text in a MS Word document, it works fine for point 1 but not point 2:
1. John Alter
I search for Alter or John, it highlights John/Alter - works.
2. I am going to school
I search for going, it highlights going but it changes its order as I am to school going - does not work.
How to fix point 2? Below is my code.
private void HighLightText(Paragraph paragraph, string text)
{
string textOfRun = string.Empty;
var runCollection = paragraph.Descendants<DocumentFormat.OpenXml.Wordprocessing.Run>();
DocumentFormat.OpenXml.Wordprocessing.Run runAfter = null;
//find the run part which contains the characters
foreach (DocumentFormat.OpenXml.Wordprocessing.Run run in runCollection)
{
if (!string.IsNullOrWhiteSpace(paragraph.InnerText) && paragraph.InnerText != "\\s")
textOfRun = run.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Text>().Text;
if (textOfRun.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
{
//remove the character from this run part
run.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Text>().Text = Regex.Replace(textOfRun, text, string.Empty, RegexOptions.IgnoreCase);//textOfRun.Replace(text, string.Empty);
runAfter = run;
break;
}
}
//create a new run with your customization font and the character as its text
DocumentFormat.OpenXml.Wordprocessing.Run HighLightRun = new DocumentFormat.OpenXml.Wordprocessing.Run();
DocumentFormat.OpenXml.Wordprocessing.RunProperties runPro = new DocumentFormat.OpenXml.Wordprocessing.RunProperties();
Highlight highlight = new Highlight() { Val = HighlightColorValues.Yellow };
DocumentFormat.OpenXml.Wordprocessing.Text runText = new DocumentFormat.OpenXml.Wordprocessing.Text() { Text = text };
runPro.Append(highlight);
HighLightRun.Append(runPro);
HighLightRun.Append(runText);
//insert the new created run part
paragraph.InsertAfter(HighLightRun, runAfter);
}
You need to split-up your Run if you want to highlight some text in the middle of that Run. So replacing the search text with an empty string won't work.
Your original text structure looks like:
<Run>
<Text>
I am going to school
</Text>
</Run>
If you want to highlight the going word, you need to make a more complex structure out of it:
<Run>
<Text>
I am
</Text>
</Run>
<Run>
<Text>
going
</Text>
</Run>
<Run>
<Text>
to school
</Text>
</Run>
Then, the Run in the middle can be set-up for highlighting.
Here is a working code sample. Please note, there's no error handing in this code! It should give you some idea how to solve your task. Implement the proper exception handing for production usage!
Also note that this sample only searches for the first occurrence, as it is in your code. If you need to highlight multiple search matches, you will have to improve this code.
void HighLightText(Paragraph paragraph, string text)
{
// Search for a first occurrence of the text in the text runs
var found = paragraph
.Descendants<Run>()
.Where(r => !string.IsNullOrEmpty(r.InnerText) && r.InnerText != "\\s")
.Select(r =>
{
var runText = r.GetFirstChild<Text>();
int index = runText.Text.IndexOf(text, StringComparison.OrdinalIgnoreCase);
// 'Run' is a reference to the text run we found,
// TextNode is a reference to the run's Text object,
// 'TokenIndex` is the index of the search string in run's text
return new { Run = r, TextNode = runText, TokenIndex = index };
})
.FirstOrDefault(o => o.TokenIndex >= 0);
// Nothing found -- escape
if (found == null)
{
return;
}
// Create a node for highlighted text as a clone (to preserve formatting etc)
var highlightRun = found.Run.CloneNode(true);
// Add the highlight node after the found text run and set up the highlighting
paragraph.InsertAfter(highlightRun, found.Run);
highlightRun.GetFirstChild<Text>().Text = text;
RunProperties runPro = new RunProperties();
Highlight highlight = new Highlight { Val = HighlightColorValues.Yellow };
runPro.AppendChild(highlight);
highlightRun.InsertAt(runPro, 0);
// Check if there's some text in the text run *after* the found text
int remainderLength = found.TextNode.Text.Length - found.TokenIndex - text.Length;
if (remainderLength > 0)
{
// There is some text after the highlighted section --
// insert it in a separate text run after the highlighted text run
var remainderRun = found.Run.CloneNode(true);
paragraph.InsertAfter(remainderRun, highlightRun);
var textNode = remainderRun.GetFirstChild<Text>();
textNode.Text = found.TextNode.Text.Substring(found.TokenIndex + text.Length);
// We need to set up this to preserve the spaces between text runs
textNode.Space = new EnumValue<SpaceProcessingModeValues>(SpaceProcessingModeValues.Preserve);
}
// Check if there's some text *before* the found text
if (found.TokenIndex > 0)
{
// Something is left before the highlighted text,
// so make the original text run contain only that portion
found.TextNode.Text = found.TextNode.Text.Remove(found.TokenIndex);
// We need to set up this to preserve the spaces between text runs
found.TextNode.Space = new EnumValue<SpaceProcessingModeValues>(SpaceProcessingModeValues.Preserve);
}
else
{
// There's nothing before the highlighted text -- remove the unneeded text run
paragraph.RemoveChild(found.Run);
}
}
This code works for highlighting the I, going, or school words in the I am going to school sentence.

Replace to Colored Text

So I want to use the Text.Replace function to replace non-colored text with colored text, but that non-colored text has to be a specific word.
eg.
private void timer1_Tick(object sender, EventArgs e)
{
int index = 0;
while(index < richTextBox1.Text.LastIndexOf(textBox1.Text))
{
richTextBox1.Find("if");
richTextBox1.Text.Replace("if", Text.Color.DarkGreen "if");
}
}
}
The coloring is not so hard but defining the term 'word' is not so simple..
Here is an example for both:
I assume that you can live with the Regex definition of word boundaries. You may need to improve on the wordPattern depending on your needs, i.e. on your grammar! (See here for an example!)
int ColorWord(RichTextBox rtb, string word, Color foreColor, Color backColor)
{
string wordPattern = #"\b" + word + #"\b";
var matches = Regex.Matches(rtb.Text, wordPattern);
for (int i = matches.Count - 1; i >= 0; i--)
{
var m = matches[i];
rtb.SelectionStart = m.Index;
rtb.SelectionLength = m.Length;
rtb.SelectionColor = foreColor;
rtb.SelectionBackColor = backColor;
}
return matches.Count;
}
This function will set fore- and backcolor for each occurance of a given word in a RichTextBox. It transverses the matches from the back just for good measure as we don't really modify any text, so the letters won't shift; so we could have looped from start to end as well; but maybe you want to adapt it one day for changing words..
Once you understand how coloring (or formatting in any other way) the RTB text works, i.e. by first selecting a portion of the text and then changing the RTB.SelectedXXX property, it should be simple to modify..
Note that the word boundaries are meant to work with normal text. It is up to you to define maybe new rules to include or exclude characters for the language you want to highlight..

Displaying coloured text in a RichTextbox

so i have a string of characters that are typically formatted like this:
" text" where the bold character is red.
i want to display these types of strings in a richtextbox with the bold character being red.
i have the text in a string variable and i have the location of the red character (in the string) in an int variable.
my solution is:
get characters before the red character
get red character
get characters after the red character
display characters before the red character
display red character (with foreground = Brushes.Red)
display characters after the red characters
this is what i've got so far:
https://github.com/icebbyice/rapide/blob/master/rapide/SpreadWindow.xaml.cs
find: "//stackoverflow" (also, seperateOutputs is not completed)
i stopped there because i thought there had to be a more efficient way to do it because i will be changing the content of the rich text box often (up to 1000 content changes / 60 seconds).
so, is there a better way to do this?
You could do this:
// getting keywords/functions
string keywords = #"\b(e)\b";
MatchCollection keywordMatches = Regex.Matches(codeRichTextBox.Text, keywords);
// saving the original caret position + forecolor
int originalIndex = codeRichTextBox.SelectionStart;
int originalLength = codeRichTextBox.SelectionLength;
Color originalColor = Color.Black;
// MANDATORY - focuses a label before highlighting (avoids blinking)
menuStrip1.Focus();
// removes any previous highlighting (so modified words won't remain highlighted)
codeRichTextBox.SelectionStart = 0;
codeRichTextBox.SelectionLength = codeRichTextBox.Text.Length;
codeRichTextBox.SelectionColor = originalColor;
// scanning...
foreach (Match m in keywordMatches)
{
codeRichTextBox.SelectionStart = m.Index;
codeRichTextBox.SelectionLength = m.Length;
codeRichTextBox.SelectionFont = new Font(codeRichTextBox.Font, FontStyle.Bold);
}
// restoring the original colors, for further writing
codeRichTextBox.SelectionStart = originalIndex;
codeRichTextBox.SelectionLength = originalLength;
codeRichTextBox.SelectionColor = originalColor;
codeRichTextBox.SelectionFont = new Font(codeRichTextBox.Font, FontStyle.Regular);
// giving back the focus
codeRichTextBox.Focus();
That belongs in the RichTextBox TextChanged Event
If you type e, it will display it Bold. Any other text will be displayed as Font.Regular
If you would like to change the syntax from e, then look at the keywords string
This is all I have for this, I hope it helps you :)

RichTextBox SelectionStart offset with linebreaks

I'm using a RichTextBox for coloured text. Let's assume I want to use different colours for different portions of the text. This is working fine so far.
I'm currently having a problem with the SelectionStart property of the RichTextBox. I've set some text to the Text property of the RichTextBox. If the text contains \r\n\r\n the SelectionStart Position won't match the position of characters with the assigned String.
Small example (WinformsApplication. Form with a RichTextBox):
public Form1()
{
InitializeComponent();
String sentence1 = "This is the first sentence.";
String sentence2 = "This is the second sentence";
String text = sentence1 + "\r\n\r\n" + sentence2;
int start1 = text.IndexOf(sentence1);
int start2 = text.IndexOf(sentence2);
this.richTextBox1.Text = text;
String subString1 = text.Substring(start1, sentence1.Length);
String subString2 = text.Substring(start2, sentence2.Length);
bool match1 = (sentence1 == subString1); // true
bool match2 = (sentence2 == subString2); // true
this.richTextBox1.SelectionStart = start1;
this.richTextBox1.SelectionLength = sentence1.Length;
this.richTextBox1.SelectionColor = Color.Red;
this.richTextBox1.SelectionStart = start2;
this.richTextBox1.SelectionLength = sentence2.Length;
this.richTextBox1.SelectionColor = Color.Blue;
}
The RichTextBox looks like this:
As you can see, the first two characters of the second sentence are not coloured. This is the result of an offset produced by \r\n\r\n.
What is the reason for this? Should I use another control for colouring text?
How do I fix the problem in a reliable way? I've tried replacing the "\r\n\r\n"with a String.Empty, but that produces other offset problem.
Related question:
Inconsistent behaviour between in RichTextBox.Select with SubString method
It seems that the sequence \r\n counts for one character only when doing selections. You can do the measurements in a copy of the string where all \r\n are replaced by \n.
Just for completeness (I'll stick to linepogls answer for now):
I've found another way to get indices for the SelectionStart property. The RichTextBox offers a Find method, that can be used to retrieve index positions based on a specified string.
Be aware of the fact, that the text you want to highlight might not be unique and occur multiple times. You can use an overload to specify a start position for the search.

label wont add to Panel control at runtime (but i dont get any errors)

well, the title pretty much says it all.
here's a little info:
i have a text file, and inside that textfile contains:
Label
"this text will be associated with the Text property of the label once the label has been added to the Panel Control at runtime."
20, 45
Trebuchet MS
14.0
explanation of the above
the above file may have only one label-info, or it may have a thousand of them. here's how it works:
Control
Text Property
Locatoin
Font Name
Font Size
...Now, all is well if i leave out the font information from the file. but as soon as i modified my regex to suit for the font name and font size, my program no longer adds the control to the Panel at runtime. there are no debugging errors or ugly squigly lines either. It just doesn't show anything.
So, my guess is the last part of the regex that tries to match font information/ can somebody please help me understand/solve this problem?
the code i have is:
public partial class Form1 : Form
{
private void FillCanvas()
{
canvas.Controls.Clear();
canvas.Visible = true;
canvas.BringToFront();
txt.Visible = false;
MatchCollection lines = Regex.Matches(File.ReadAllText(Path), #"(.+?)\r\n""([^""]+)""\r\n(\d+), (\d+)\r\n""(\w*)\r\n" +
#"(\d+)");
foreach (Match match in lines)
{
string control = match.Groups[1].Value;
string text = match.Groups[2].Value;
int x = Int32.Parse(match.Groups[3].Value);
int y = Int32.Parse(match.Groups[4].Value);
string fname = match.Groups[6].Value;
float fsize = float.Parse(match.Groups[7].Value);
switch (control)
{
case "Label":
Label label = new Label();
label.Text = text;
label.AutoSize = true;
label.IsAccessible = true;
label.Font = new Font(fname, fsize);
label.MouseMove += new MouseEventHandler(label_MouseMove);
label.MouseDown += new MouseEventHandler(label_MouseDown);
label.MouseUp += new MouseEventHandler(label_MouseUp);
label.Click += new EventHandler(label_Click);
label.DoubleClick += new EventHandler(label_DoubleClick);
canvas.Controls.Add(label);
ControlCount++;
label.Name = ControlCount.ToString();
label.Location = new Point(x, y);
SelectedControl = label.Name;
break;
Your regex pattern is incorrect. Change it to this:
#"(.+?)\r\n""([^""]+)""\r\n(\d+),\s(\d+)\r\n([\w\s]*)\r\n(\d+)"
Your original pattern has 2 problems when it attempts to match the font name. First, it has unnecessary quotation marks, but there are none for the font name section. Second, the font name can have a space in it, and your pattern didn't account for it.
Your pattern:
#"(.+?)\r\n""([^""]+)""\r\n(\d+), (\d+)\r\n""(\w*)\r\n(\d+)"
^ ^
| |
1st problem ___| |
2nd problem ___|
BTW, the current pattern does not correctly capture the "14.0" font size. It will capture "14" only. If you need to capture the rest of it, consider using (\d+(?:\.\d+)?) instead if it's an optional format. If you always expect it to be in that format then use (\d+\.\d+).
EDIT: if it's within your control I suggest switching the text file / regex approach to an XML format instead. Some nice LINQ to XML will make this simpler to maintain.
Have you set a breakpoint inside the foreach and debugged? Since you are iterating over the results of a MatchCollection, the first place I'd check is to see if you have zero matches.

Categories