RTF Replace, wrong Colors [duplicate] - c#

Cannot keep the highlighted effect I set in my RichTextBox on my text after removing content of a line in front of him.
No matter how much text I remove from the control it always removes the custom SelectionColor and SelectionBackColor I set to a text already contained in it.
Code of my Removal method:
private void btnRemove_Click(object sender, EventArgs e)
{
//Remove selected line from RichTextBox
richTextBox1.Text = richTextBox1.Text.Remove(richTextBox1.Text.Length - 1, 1);
//Remove all blank lines remaining after deletion
richTextBox1.Text = Regex.Replace(richTextBox1.Text, #"^\s*$(\n|\r|\r\n)", "", RegexOptions.Multiline);
}
The number of letters I want to remove here is 1 as the word "AND" is a simple image inserted by means of Clipboard Paste method.

You must never (read my lips: Never, never, never) change to Text or the Lines property of a RichtTextBox or else you will lose/mess up all previous formatting.
So you need to change this:
richTextBox1.Text = richTextBox1.Text.Remove(richTextBox1.Text.Length - 1, 1);
To this sequence:
First Select the part of the Text you want to change in some way:
richTextBox1.SelectionStart = richTextBox1.Text.Length - 1;
richTextBox1.SelectionLength = 1;
Now you can change it. To delete either use:
richTextBox1.SelectedText = "";
or
richTextBox1.Cut();
The latter version also will place the text in the clipboard; doing it it will keep the formatting of that portion and you could Paste it to some other place..
The same rules apply when you want to add or change any type of formatting:
First Select Then Modify
And, yes, this means that the second command will grow quite a bit, i.e. you will have to replace the RegEx.Replace by a loop :-(

Related

richTextBox - add text and table

i want to add formatted text and table to a richTextBox.
For this I use these codes:
Text:
richTextBox1.SelectionFont = new Font("Maiandra GD", 30, FontStyle.Bold);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.SelectionIndent = 0;
richTextBox1.AppendText("text text text");
And the table:
StringBuilder tableRtf = new StringBuilder();
tableRtf.Append(#"{\rtf1\fbidis\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}");
for (int j = 0; j <5; j++)
{
tableRtf.Append(#"\trowd");
tableRtf.Append(#"\cellx2500" + " ghhghgjghjghjhggjh");
tableRtf.Append(#"\intbl\cell");
tableRtf.Append(#"\cellx10000\intbl\cel");
tableRtf.Append(" " + "sdfsdfs" + #"\intbl\clmrg\cell\row");
}
tableRtf.Append(#"\pard");
tableRtf.Append(#"}");
richTextBox1.Rtf=tableRtf.ToString();
But the
richTextBox1.Rtf=tableRtf.ToString();
kills the previous contents.
How can I make compatible them?
It is not a duplicate because I want two thing:
1) add formatted text to richTextBox this way:
richTextBox1.SelectionFont = new Font("Maiandra GD", 30, FontStyle.Bold);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.AppendText("text text text");
It is well readable and I can modify easily.
2) And I want to add tables.
So the structure:
text text text text text
text text text text text
|TABLE|
text text text text text
text text text text text
text text text text text
|TABLE|
etc.
But I don't know how can I apply tables without losing previous contents?
What you need to do is to dissect the rtf codes into the headers and the bodies.
The table body starts with the loop and keeping the \par is surely a good idea.
But you must neither replace the old text nor simply append the body to the end.
Instead you need to insert it before the last curly! This is because that last curly marks the end of the whole rtf code and anything after it will be ignored!
This was simple.
For a full solution you also will want to combine the headers.
This is a lot more work and writing it all out would go beyond an SO answer.
But the basic principle is simple:
You need to understand the things your table header adds to the things already in the primal header.
The most common things are afont table and a color table.
So if you want to use one or more different fonts in the appended table you need to do this:
add them to the font table with a new font index, e.g. as \f1\fnil Consolas; after the previous semicolon.
use it by changing the loop to include the new font right after the first \intbl table-paragraph-formatting control word: \intbl\f2\fs24 ghhghgjghjghjhggjh.
repeat as needed if you want to use different fonts in the table.
add a cfNfont color selector, if you want to.
The same idea will also work for the color table. It doesn't have a explicit indexing, so order matters; all colors are appended, each with a semicolon at the end:
{\colortbl ;\red255\green0\blue0;\red25\green0\blue220;}
..adds a blue color to the red from the formatted text.
You see, this is work and takes some effort and preparations.
You can find the full rtf specs here.
Here is a screenshot of playing a little with the rtf..:
Note that the parts of table header was created by the control; you may want to use a dummy control for this or maybe you can figure out which parts you need and which are not necessary..
Update: Here is a 'appending rtf for dummies' version:
tableRtf.Append(#"{\fonttbl{\f0\fnil\fcharset0 Courier;}}");
for (int j = 0; j <5; j++)
{
tableRtf.Append(#"\trowd");
tableRtf.Append(#"\cellx2500" + " ghhghgjghjghjhggjh");
tableRtf.Append(#"\intbl\cell");
tableRtf.Append(#"\cellx10000\intbl\cel");
tableRtf.Append(" " + "sdfsdfs" + #"\intbl\clmrg\cell\row");
}
tableRtf.Append(#"\pard");
tableRtf.Append(#"}");
string rtf1 = richTextBox1.Rtf.Trim().TrimEnd('}');
string rtf2 = tableRtf.ToString();
richTextBox1.Rtf = rtf1 + rtf2;
Note that the font table inserted before the table body does work! But make sure not to add the rtf-start tag!!

Retain highlighted color of text after editing

Cannot keep the highlighted effect I set in my RichTextBox on my text after removing content of a line in front of him.
No matter how much text I remove from the control it always removes the custom SelectionColor and SelectionBackColor I set to a text already contained in it.
Code of my Removal method:
private void btnRemove_Click(object sender, EventArgs e)
{
//Remove selected line from RichTextBox
richTextBox1.Text = richTextBox1.Text.Remove(richTextBox1.Text.Length - 1, 1);
//Remove all blank lines remaining after deletion
richTextBox1.Text = Regex.Replace(richTextBox1.Text, #"^\s*$(\n|\r|\r\n)", "", RegexOptions.Multiline);
}
The number of letters I want to remove here is 1 as the word "AND" is a simple image inserted by means of Clipboard Paste method.
You must never (read my lips: Never, never, never) change to Text or the Lines property of a RichtTextBox or else you will lose/mess up all previous formatting.
So you need to change this:
richTextBox1.Text = richTextBox1.Text.Remove(richTextBox1.Text.Length - 1, 1);
To this sequence:
First Select the part of the Text you want to change in some way:
richTextBox1.SelectionStart = richTextBox1.Text.Length - 1;
richTextBox1.SelectionLength = 1;
Now you can change it. To delete either use:
richTextBox1.SelectedText = "";
or
richTextBox1.Cut();
The latter version also will place the text in the clipboard; doing it it will keep the formatting of that portion and you could Paste it to some other place..
The same rules apply when you want to add or change any type of formatting:
First Select Then Modify
And, yes, this means that the second command will grow quite a bit, i.e. you will have to replace the RegEx.Replace by a loop :-(

How to move the cursor in multiline textbox from the end of the line to the beginning of the next one?

I have a multiline textbox with wordwrap on, while I want it's cursor to be located in the beginning of the next line when it arrives at the end of the current one.
e.g. - if 8 characters can be entered in line (monospaced), and I enter this:
12345678
I would like the cursor to be under the '1' char (and not after the 8).
The challenge is: I can't use NewLine as a part of the textbox string.
You could subscribe for notifications when the textbox changes, and if there's 8 characters add an Environment.NewLine to the text. After the first line you'd have to do a split on the text to only get the last line but that's not too hard. The user will always be able to manually move the cursor to the end of the line again though so you'd need to add logic to check if there are 9 characters on the line and delete the last one.
The thing that finally solved my problem, while taking into account the limitation of NOT using Environment.NewLine was adding one more additional space, save the SelectionStart position, and calculate the correct one without considering the redundant space (see another post about How to make wordwrap consider a whitespace in a string as a regular character?)
This solution fits for a very special case in which I've created my own "NewLine" for our use which will occur while the user will click Alt+Enter - The amount of relevant spaces will be added to fill a line (then I needed the cursor to go down... this was the issue!):
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
var tb = sender as TextBox;
if (Keyboard.Modifiers == ModifierKeys.Alt && Keyboard.IsKeyDown(Key.Enter))
{
int origSelectionStart = tb.SelectionStart;
int paddingSpacesAmountToAdd = WidthInCells - (tb.Text.Substring(0, tb.SelectionStart)).Length%WidthInCells;
int origPaddingSpacesAmountToAdd = paddingSpacesAmountToAdd;
// Only if the cursor is in the end of the string - add one extra space that will be removed eventually.
if (tb.SelectionStart == tb.Text.Length) paddingSpacesAmountToAdd++;
string newText = tb.Text.Substring(0, tb.SelectionStart) + new String(' ', paddingSpacesAmountToAdd) + tb.Text.Substring(tb.SelectionStart + tb.SelectionLength);
tb.Text = newText;
int newSelectionPos = origSelectionStart + origPaddingSpacesAmountToAdd;
tb.SelectionStart = newSelectionPos;
e.Handled = true;
}
else if (Keyboard.IsKeyDown(Key.Enter))
{
//source already updated because of the binding UpdateSourceTrigger=PropertyChanged
e.Handled = true;
}
}
That won't be enough (as I mentioned above - there's a spaces issue), so:
void _textBox_TextChanged(object sender, TextChangedEventArgs e)
{
int origCursorLocation = _textBox.SelectionStart;
// Regular Whitespace can't be wrapped as a regular character, thus - we'll replace it.
_textBox.Text.Replace(" ", "\u00A0");
_textBox.SelectionStart = origCursorLocation;
}
Important - This solution works for MonoSpace characters when there's a calculation of the exact fitting fontsize.

C# Cursor/ Caret Not display when Append text in Richtextbox

I am new to Programming, i am making C# Windows Form Application were, on selecting Tree node, it append the text in Richtextbox:
Qs1: For me Caret is not displayed after selecting the tree Node.
Qs2: Make display like editor, where if word start with // ( Comment) should be in green color.
Thanks
if (treeView1.SelectedNode.Name == "Node1")
{
this.richTextBox1.SelectedText += " my text for Node1" + Environment.NewLine
richTextBox1.Focus();
}
else if (treeView1.SelectedNode.Name == "Node2")
{
this.richTextBox1.SelectedText += " my text for Node2" + Environment.NewLine
richTextBox1.Focus();
}
You're asking two questions related to RichTextBox. The preferred form on StackOverflow is one question per question. You'll probably get more responses with more focused questions.
That being said:
According to the documentation for the Select method:
The text box must have focus in order for the caret to be moved.
So you need to do that first.
In addition, as a general rule, you should never modify the pre-existing Text or SelectedText with += because this will clear away any and all RTF formatting on that text. Instead, to insert text, you should set the selection to the desired location, with length zero, and insert there. Thus:
public static void FocusAndAppendToSelectedText(this RichTextBox richTextBox, string text)
{
Action append = () =>
{
richTextBox.Focus();
var start = richTextBox.SelectionStart;
var length = richTextBox.SelectionLength;
var insertAt = start + length;
richTextBox.Select(insertAt, 0);
richTextBox.SelectedText = text;
};
if (richTextBox.InvokeRequired)
richTextBox.BeginInvoke(append);
else
append();
}
Also, you should use \n rather than Environment.Newline because the latter will get simplified into the former anyway.
A question like "[How to] Make display like editor, where if word start with // ( Comment) should be in green color" is very general. Try to break it down into discrete issues and ask questions for those you can't figure out yourself. To get you started, see this question here: highlight the '#' until line end in richtextbox. However, you may want to set the SelectionBackColor not the SelectionColor, depending on your precise UI requirements.

How do I convert text to hyperlink in a WPF RichTextBox FlowDocument?

I'm trying to turn some text to Hyperlink while a user is typing in WPF RichTextBox.
My first attempt at it involves running this code at each KeyUp:
Regex r = new Regex("[A-Z]{3}");
FlowDocument doc = this.inputBox.Document;
String text = new TextRange(doc.ContentStart, doc.ContentEnd).Text;
foreach (Match m in r.Matches(text))
{
TextPointer start = doc.ContentStart.GetPositionAtOffset(m.Index + 2);
TextPointer end = doc.ContentStart.GetPositionAtOffset(m.Index + m.Length + 2);
Hyperlink sp = new Hyperlink(start, end);
}
This runs correctly the first time a user enters a sequence of 3 capital letters, ABC, but hyperlink creation fails with an exception when a second sequence is entered. Looking at the variable while debugging, it appears that the two TextPointers keep pointing at the first sequence.
I think your problem might be due to a common misunderstanding of what the GetPositionAtOffset() method does.
GetPositionAtOffset returns the offset within the FlowDocument markup, not the visible text character offset.
After you insert your first hyperlink, the FlowDocument contains opening and closing tags:
<Hyperlink>the_regex_match</Hyperlink>
This means that the FlowDocument offsets no longer align with the plain text character indexes returned by the RegEx match.

Categories

Resources