C# WPF RichTextBox restrict text formatting to visible text - c#

I have a RichTextBox displaying a FlowDocument that is large (>10k lines). I am attempting to apply text formatting to the entire Document. This is taking a while to complete.
Is there any way to focus the formatting on the visible parts of the Document only?
For Information: I am attempting to search through the contents of the RichTextBox and highlight all matching occurrences. The searching function is baised upon this one. I am using the following code to 'highlight' each match found.
protected void ColorTextRanges(Color color)
{
foreach ( var textRange in locatedInstances )
{
if ( textRange != null )
{
textRange.ApplyPropertyValue( TextElement.BackgroundProperty, new SolidColorBrush( color ) );
}
}
}

Rather than create the brush in the loop create it outside and reuse it. Not going to be major but should help a little. And you might test for the BackgroundProperty and only set it if it is wrong - this might make it slower but if most of the document is already the right color then it should help.
protected void ColorTextRanges(Color color)
{
SolidColorBrush brush = new SolidColorBrush( color );
foreach ( var textRange in locatedInstances )
{
if ( textRange != null )
{
textRange.ApplyPropertyValue( TextElement.BackgroundProperty, brush);
}
}
}

The best performance increase I found was to update the document when out wasn't displayed on the screen. Not sure sure why this is but I can guess that some thing in the screen buffer isn't being updated.

Related

How do I get the start and end index from a selection inside RichTextBox?

I have a rich text box that I'm allowing user to highlight text. Text being loaded is coming from a simple plain text file. But I need to store the absolute start and end character position (relative to beginning of document) of the highlighted text so that when they save it, it can reload with highlights.
So far I can do this to apply the highlighting
private void textBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
HighlightWordInTextBox(this.textBox, this.textBox.Selection.ToString(), new SolidColorBrush(Colors.Yellow));
}
public void HighlightWordInTextBox(RichTextBox textbox, string word, SolidColorBrush color)
{
TextRange tr = new TextRange(this.textBox.Selection.Start, this.textBox.Selection.End);
tr.ApplyPropertyValue(TextElement.BackgroundProperty, color);
}
But I do not see anywhere inside the Start or End objects of Selection anything that provides the character position? Almost all the methods return another TextPointer - but how do you get the character position from a TextPointer?
Assuming all the text is loaded into a single
this.textBox.Document.Blocks.Add(new Paragraph(new Run(fullText)));
EDIT:
In the immediate window when debugging I can access something called CharOffset and Offset but cannot do so in the source code, it gives a compile error. Also those properties although present when inspecting the object at runtime, they are not in the documentation.
And yet...
You can find the index of the Selection's start and end with this...
var docStart = textBox.Document.ContentStart;
var selectionStart = textBox.Selection.Start;
var selectionEnd = textBox.Selection.End;
//these will give you the positions needed to apply highlighting
var indexStart = docStart.GetOffsetToPosition(selectionStart);
var indexEnd = docStart.GetOffsetToPosition(selectionEnd);
//these values will give you the absolute character positions relative to the very beginning of the text.
TextRange start = new TextRange(docStart, selectionStart);
TextRange end = new TextRange(docStart, selectionEnd);
int indexStart_abs = start.Text.Length;
int indexEnd_abs = end.Text.Length;

RichText GUI selection color issue when GUI minimized

I have simple Win-form GUI in C# which display the text in red or Green depended upon the value received. The RichText display the text correctly as long as the i do not minimize the GUI. When the GUI is minimized, the text shown in Text window is in black color (only data that was processed when GUI was minimized). when the GUI is maximized the text color for the data shown correctly again.
Please let me know what is wrong here.
Here is my code:
LogMessageWindow.Find(message);
LogMessageWindow.SelectionColor = Color.Red; /// if message&2==0 set color to Red otherwise set color to green
LogMessageWindow.SuspendLayout();
LogMessageWindow.Focus();
LogMessageWindow.AppendText(message + ".\n");
LogMessageWindow.ScrollToCaret();*
In your code you have:
LogMessageWindow.Find(message);
This line is useless: you are Appending a new chunk of Text. Searching for it before appending it won't do much (maybe locate an identical string. Then what?).
LogMessageWindow.SuspendLayout();
SuspendLayout() can be useful if you're adding/appending a large selection of lines of text in batch. When you're finished, you should ResumeLayout(). Doesn't seem to be needed here.
LogMessageWindow.Focus();
Moving the Focus on the RTB control doesn't accomplish anything special. And if the container Form is minimized... Since you're adding text in a procedure, the focus is not needed.
A couple of things you can do.
Using a method, pass a reference to the RichTextBox that is used for this task, the color to use and the text to be appended. Here the new text color is defined as Color? color, so if you pass null, the control ForeColor is used.
RTBAppendWithColor(LogMessageWindow,
((message & 2) == 0) ? Color.Red : Color.Green,
message.ToString() + "\n");
private void RTBAppendWithColor(RichTextBox rtb, Color? color, string AppendedText)
{
int sLenght = AppendedText.Length;
rtb.AppendText(AppendedText);
rtb.Select(rtb.Text.Length - sLenght, sLenght);
if (color != null)
rtb.SelectionColor = (Color)color;
rtb.ScrollToCaret();
}
Using an Extension.
Create a static Class with a static Method that references a RichTextBox object. This method will be a new method of any RichTextBox you create.
LogMessageWindow.AppendWithColor(((message & 2) == 0) ? Color.Red : Color.Green,
message.ToString() + "\n");
public static class RTBExtensions
{
public static void AppendWithColor(this RichTextBox rtb, Color? color, string AppendedText)
{
int sLenght = AppendedText.Length;
rtb.AppendText(AppendedText);
rtb.Select(rtb.Text.Length - sLenght, sLenght);
if (color != null)
rtb.SelectionColor = (Color)color;
rtb.ScrollToCaret();
}
}
If you using FrameWork 3.5, the selection text will probably remain selected even after ScrollToCaret() is called. If it looks ugly, add:
rtb.SelectionStart = rtb.Text.Length;
before rtb.ScrollToCaret().
Thanks al for yur valuable feedback. i was able to get this done by suing this code.
LogMessageWindow.SelectionStart = LogMessageWindow.TextLength; LogMessageWindow.SelectionLength = 0; LogMessageWindow.SelectionColor = Color.Red; LogMessageWindow.SuspendLayout();LogMessageWindow.AppendText(message + ".\n"); LogMessageWindow.ScrollToCaret(); ` LogMessageWindow.ResumeLayout()

Bullets and pasting into flow document

I need to paste some rtf text from a datagrid into a flow document. I want to paste it at the caret position in the richtextbox. This works except when i have a bullet or Numbered list. It will paste it before the bullet or number.
I have searched every where for this with no luck.
private void dgStandardText_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
if (dgStandardText.SelectedItems.Count > 0) {
foreach(DataRowView row in dgStandardText.SelectedItems) {
byte[] byteArray = Encoding.ASCII.GetBytes(row[3].ToString());
using(MemoryStream ms = new MemoryStream(byteArray)) {
TextRange tr = new TextRange(txtAreaText.CaretPosition, txtAreaText.CaretPosition);
tr.Load(ms, DataFormats.Rtf);
}
}
}
NeedSave = true;
dgStandardText.SelectedItems.Clear();
}
Before Paste
After Paste
This seems to happen when the content you're pasting includes block elements, as opposed to inline elements. For plain text, WPF seems to treat newlines (e.g., \r\n) as paragraph breaks, so multi-line content would be translated into multiple blocks. What you want to do is insert just the inline content from each of those source blocks, such that it goes into the current block of the target document (in this case, a list item). Do this for the first source block, then insert a paragraph break, update the caret position, and move on to the next block.
Is the content of the grid row actually RTF content, or is it plain text? If it's plain text, then this should be pretty straightforward. Try changing your foreach loop body to something like this:
var text = row[3].ToString();
var lines = text.Split(
new[] { '\r', '\n' },
StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var p = txtAreaText.CaretPosition;
txtAreaText.BeginChange();
p.InsertTextInRun(line);
p = p.InsertParagraphBreak();
txtAreaText.EndChange();
txtAreaText.CaretPosition = p;
}
Now, if the source content really is RTF, then things will get more complicated. I suspect you'll need to load it into an in-memory flow document and walk through the elements to figure out where the paragraph breaks are. For each paragraph, locate the inline contents that intersect the selection range, and insert them into the target document. Insert a paragraph break when you reach the end of each paragraph in the source range, just like in the example above.
I'm afraid the Documents API is the one area of WPF that I haven't explored very thoroughly, so I can only give you a general idea of what to do. Perhaps someone else can provide more detail.

Highlighting whole lines in RichTextBox

I'm trying to highlight a whole line in a RichTextBox. Right now I have this method
if (Current != null)
{
selectStart = this.textBox.Text.Length;
foreach (string s in Current.Details)
{
this.textBox.AppendText(s + Environment.NewLine);
}
selectEnd = this.textBox.Text.Length;
this.textBox.Select(selectStart, selectEnd - selectStart);
this.textBox.SelectionBackColor = Color.FromArgb(51, 255, 51);
}
But this results with only highlighting the text, as shown here:
I want the code to highlight the whole line length. Is there a way to do that ?
I don't think you can do what you want to do as the RichTextBox does not support the box model (like browsers would with a <div> block). It will only ever decorate the text itself.

C# Ways to handle dynamic font sizing

What would be a good way to dynamically change the font size on my application? I have many screens with many labels. Those labels are at least inheriting from a common label. The other issue is should I leave the labels to autosize and just use the line breaks in the label so that it can break up? I've switched many of the labels to not be autosized because it was going wide and not wrapping itself.
Currently I have everything set to anchor, etc. and any of the buttons and such will be fine. It's just the font now that needs to be sized dyanmically.
Thanks!
Before InitializeComponent(); in each form's constructor, simply put this.Font = new Font( ... ); as you desire. However, it will only cascade through the controls if you left each control at the default. You can always put a loop after the initialization:
foreach(Control c in this.Controls)
{
if(c is Label) //if you want to change Labels only
c.Font = new Font( ... );
}
If it makes things look weird, change your AutoScaleMode and related properties.
To address the question of how to handle wrapping the label text, use Label1.AutoSize = true, and simply set Label1.MaximumSize = new Size(x, 0);, where x is your maximum width.
That all said, if you're going to be dynamically scaling things often, you really should look into using WPF instead of WinForms. It has more ability to handle these types of tasks automatically.
You can save the font size as an integer in the application settings.
Then your application will remember its font state upon start when you get the font size.
Properties.Settings.Default.FontSize = 3;
Properties.Settings.Default.Save();
Then as said above use a foreach loop.
foreach(Control c in this.Controls)
{
c.Font = new Font( .. );
// if(c is Panel)
// {
// foreach(Control d in c.Controls)
// {
// d.Font = new Font( .. );
// }
}
}

Categories