How can i focus a Inline in a RichTextBox?
I Create a FlowDocument from a Text-File and load it in my richTextBox1
and mark one Inline after an other accordingly to a Button_click (be recreating the FlowDocument)
with this code:
richTextBox1.SelectAll();
richTextBox1.Selection.Text = "";
string text = System.IO.File.ReadAllText(file);
int iZeile = 0;
string[] split = text.Split(new string[] {"\r\n"},StringSplitOptions.None);
foreach (string s in split)
{
if (iZeile != 27)
{
paragraph.Inlines.Add(s + "\r\n"); // adds line added without marking
}
else
{
Run run = new Run(split[27]); // adds line with marking
run.Background = Brushes.Yellow;
paragraph.Inlines.Add(run);
paragraph.Inlines.Add("\r\n");
}
iZeile++;
}
FlowDocument document = new FlowDocument(paragraph);
richTextBox1.Document = new FlowDocument();
richTextBox1.Document = document;
Keyboard.Focus(richTextBox1);
}
I know its not.. perfect.
the Issue
It works so far but the problem that occurs is me Market Inline doesn't comes intoView. Is there a easy way to bring this Inline intoView?
The straightforward solution seemed to be FrameworkContentElement.BringIntoView() but after putting it in the code below it initially had no effect. As it turns out this is one of these timing issues (I've seen similar problems in WinForms) that can be solved by processing the outstanding Windows Messages. WPF has no direct equivalent of DoEvents() but there exists a well known substitute.
I placed this in a ButtonClick, changes marked with //**:
Paragraph paragraph = new Paragraph();
Inline selected = null; //**
richTextBox1.SelectAll();
richTextBox1.Selection.Text = "";
string text = System.IO.File.ReadAllText(#"..\..\MainWindow.xaml.cs");
int iZeile = 0;
string[] split = text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
foreach (string s in split)
{
if (iZeile != 27)
{
paragraph.Inlines.Add(s + "\r\n"); // adds line added without marking
}
else
{
Run run = new Run(split[27]); // adds line with marking
run.Background = Brushes.Yellow;
paragraph.Inlines.Add(run);
paragraph.Inlines.Add("\r\n");
selected = run; // ** remember this element
}
iZeile++;
}
FlowDocument document = new FlowDocument(paragraph);
richTextBox1.Document = new FlowDocument();
richTextBox1.Document = document;
Keyboard.Focus(richTextBox1);
DoEvents(); // ** this is required, probably a bug
selected.BringIntoView(); // **
And the helper method, from here:
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Background,
new Action(delegate { }));
}
you should try one of this methods
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
.
richTextBox.AppendText(text);
richTextBox.ScrollToEnd();
futher informations are here and here
Edit
ok after a bit of digging in the WPF RichTextBox i thing you cloud try richTextBox.ScrollToVerticalOffset(Offset)
to get the Offset maybe you could use this answer
EDIT 2
ok after some more research i found following Link where you can download this working example
Related
Is it possible to highlight a part of a text without selecting this part of the text preferably with a different color in Textbox or Rich TextBox? In fact, I mean, a part of the text is highlighted by another color differing from the color assigned for text selection. To clarify, I have attached an image showing this behavior. (The image is from a website, not WPF).
The bold and dark green part is a text which is just highlighted, and the gray region is a selected part.
Using the RichTextBox element allows for more styling options which, to my knowledge, aren't available for the regular TextBox element.
Here is an approach that I have created:
// Generate example content
FlowDocument doc = new FlowDocument();
Run runStart = new Run("This is an example of ");
Run runHighlight = new Run("text highlighting in WPF");
Run runEnd = new Run(" using the RichTextBox element.");
// Apply highlight style
runHighlight.FontWeight = FontWeights.Bold;
runHighlight.Background = Brushes.LightGreen;
// Create paragraph
Paragraph paragraph = new Paragraph();
paragraph.Inlines.Add(runStart);
paragraph.Inlines.Add(runHighlight);
paragraph.Inlines.Add(runEnd);
// Add the paragraph to the FlowDocument
doc.Blocks.Add(paragraph);
// Apply to RichTextBox
YourRichTextBoxHere.Document = doc;
View Screenshot
I found this article to be helpful.
Highlight Searched Text in WPF ListView
While the article is about highlighting searched text in a ListView, I have easily adapted it in my own code to work with pretty much any control.
Starting with the control you pass in, it will recursively look for TextBlocks and will find the text you want, extract it as an inline, and will change it's Background / Foreground properties.
You can easily adapt the code to be a behavior if your want.
Here is an example:
private void HighlightText(object controlToHighlight, string textToHighlight)
{
if (controlToHighlight == null) return;
if (controlToHighlight is TextBlock tb)
{
var regex = new Regex("(" + textToHighlight + ")", RegexOptions.IgnoreCase);
if (textToHighlight.Length == 0)
{
var str = tb.Text;
tb.Inlines.Clear();
tb.Inlines.Add(str);
return;
}
var substrings = regex.Split(tb.Text);
tb.Inlines.Clear();
foreach (var item in substrings)
{
if (regex.Match(item).Success)
{
var run = new Run(item)
{
Background = (SolidColorBrush) new BrushConverter().ConvertFrom("#FFFFF45E")
};
tb.Inlines.Add(run);
}
else
{
tb.Inlines.Add(item);
}
}
}
else
{
if (!(controlToHighlight is DependencyObject dependencyObject)) return;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(dependencyObject, i), textToHighlight);
}
}
}
I hope this is helpful!
I have an array of sentences. Every sentence is a new Run object inside Inlines property of richtextbox`s FlowDocument.
Every sentence have a color.
var paragraph = new Paragraph();
foreach (var sentence in Sentences)
{
....
paragraph.Inlines.Add(new Run { Text = sentence, Background = new SolidColorBrush(color) });
}
tbText.Document.Blocks.Add(paragraph);
When I am editing the sentence like this (I changed 'yes' to 'y1111111111111es' )
I expected to get the same Run object with changed text from 'yes' to 'y1111111111111es' but instead I got 3 Run objects with 'y', '1111111111111111111' and 'es'
That`s how I retrieve the textes
foreach (Paragraph paragraph in tbText.Document.Blocks)
{
foreach (Run inline in paragraph.Inlines)
{
editedTextes.Add(inline.Text);
}
}
Is there any way to edit the text inside native Run object without populating new Run objects when I change the text
It seems FlowDocument dynamically adds runs to encourage wrapping by design.
I see from another stackoverflow post, that if you use a TextBlock instead of runs inside of Paragraphs, you can preserve your text and remove the runs and prevent wrapping.
Try this in your second block:
foreach (Paragraph paragraph in tbText.Document.Blocks)
{
var sb = new StringBuilder();
foreach (Run inline in paragraph.Inlines)
{
sb.Append(inline.Text);
}
editedTextes.Add(new TextBlock()
{
Text = sb.ToString(),
TextWrapping = TextWrapping.NoWrap
});
}
Or just add one run after you have built the string if you don't like the TextBlocks:
editedTextes.Add(sb.ToString());
This has been an issue with many of my applications and I don't know why Windows doesn't have an elegant solution for this.
I am working with Winforms in .Net 4.5 in VS2013
For example, I would like to change the color of one line of text in a multiline RichTextBox.
For this I am required to set the selection using something like
rtb.Select(rtb.GetFirstCharIndexFromLine(r), str.Length);
Then, I would set the color using
rtb.SelectionColor = Color.Red;
And presumably, cancel the selection with
rtb.DeselectAll();
Now the problem is the cursor/caret has moved back to the beginning of the line,
I try to fix it by saving the previous Caret Position,
rtb.CaretPosition
However, CaretPosition is not a method of RichTextBox, and everywhere online this is the primary method everyone uses.
I tried adding PresentationFramework to my References and to my code I added
using System.Windows.Framework;
As suggested here: http://msdn.microsoft.com/en-us/library/system.windows.controls.richtextbox.caretposition(v=vs.110).aspx
but I still do not see the CaretPosition property, only the ScrollToCaret() method.
My 2 questions are:
How do I get the CaretPosition property in my RichTextBox?
How can I change the text color without using selections and affecting the caret position, having to write complex logic to restore it for the user.
My application checks serial numbers, one per line, and highlights them red if they do not match the format, as shown below.
private void rtb_TextChanged(object sender, EventArgs e)
{
string pattern = #"[A-Z]{2}[A-Z, 0-9]{2}\d{4}";
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
TextReader read = new System.IO.StringReader(rtb.Text);
SerialNumbers.Clear();
int selectStart = 0;
for (int r = 0; r < rtb.Lines.Length; r++)
{
string str = read.ReadLine();
if (str != null)
{
selectStart += str.Length;
MatchCollection matches = rgx.Matches(str);
if (matches.Count == 1)
{
SerialNumbers.Add(str);
}
else
{
rtb.Select(rtb.GetFirstCharIndexFromLine(r), str.Length);
rtb.SelectionColor = Color.Red;
rtb.DeselectAll();
}
}
}
}
You should be using SelectionCaret (as #Mangist mentioned in a comment) because you're using WinForms and not WPF. The MSDN article you referenced only applies to WPF, which is very different from WinForms.
As an example, I use the following to easily log to a rich text box from anywhere in a WinForms app:
public static void Log(string text, ref RichTextBox rtbLogBox) {
//
if (text == null) return;
var timestamp = DateTime.Now.ToLongTimeString();
var logtext = string.Format("{0} - {1}\r\n\r\n", timestamp, text);
if (rtbLogBox.InvokeRequired) {
var logBox = rtbLogBox;
logBox.Invoke(new MethodInvoker(delegate {
logBox.AppendText(logtext);
logBox.Update();
logBox.SelectionStart = logBox.Text.Length;
logBox.ScrollToCaret();
}));
} else {
rtbLogBox.AppendText(logtext);
rtbLogBox.Update();
rtbLogBox.SelectionStart = rtbLogBox.Text.Length;
rtbLogBox.ScrollToCaret();
}
}
Notice how the ScrollToCaret() is called after setting SelectionStart to the length of text in the rich text box. This solves the 'issue' of AppendText not scrolling to the bottom after adding text.
In your case you will simply want to save the SelectionStart value before you format your text with the highlighting, and then restore it once you've finished.
Fixed it by saving SelectionStart position
int selectionStart = SNbox.SelectionStart;
SNbox.Select(SNbox.GetFirstCharIndexFromLine(r), str.Length);
SNbox.SelectionColor = Color.Red;
SNbox.DeselectAll();
SNbox.SelectionStart = selectionStart;
SNbox.SelectionLength = 0;
I have a text with hyperlinks in it.
To not irritate the reader I want to have the normal text and the links on the same line. Right now every Inline element starts a new line.
It displays:
Please visit
http://google.com
to continue.
I want:
Please visit http://google.com to continue.
I've also noticed, that the hyperlink Hit area cover the hole inline element and not just the text.
My problem is identical than described and solved here:
Add clickable hyperlinks to a RichTextBox without new paragraph
The problem is, that it seems than something like a flowdocument for wp8 doesn't exist.
I need to create the inline elements programatically.
EDIT 1:
Here my code how I add the inline elements:
int index = 0;
rt = new RichTextBox() { };
while (true)
{
Paragraph para = new Paragraph();
if (item.text.Substring(index).IndexOf("<") == 0)
{
//TRUE when link
//I extract the URL and the linktext, and also update the index
Hyperlink hyper = new Hyperlink();
hyper.Click += new RoutedEventHandler((sender,e) => Hyperlink_Click(sender,e,URL));
hyper.Inlines.Add(linktext);
para.Inlines.Add(hyper);
}
else if (item.text.Substring(index).Contains("<"))
{
//TRUE when text, item.text contains a link
// I extract the text and update index
Run run = new Run() { Text = text };
para.Inlines.Add(run);
}
else
{
//TRUE when only text is left
Run run = new Run() { Text = item.text.Substring(index) };
para.Inlines.Add(run);
rt.Blocks.Add(para);
break;
}
// REMOVE: rt.Blocks.Add(para);
}
rt.SetValue(Grid.RowProperty, MainViewer.RowDefinitions.Count - 1);
MainViewer.Children.Add(rt);
EDIT 2
I still couldn't solve this Problem, does no one know a solution? I saw what I want in an App before, so it must be possible.
EDIT 3
I've created for every inline element a new paragraph. I've fixed my code above, it is working now
Paragraph p = new Paragraph();
p.Inlines.Add("Plase visit ");
var link = new Hyperlink();
link.Inlines.Add("google.com ");
p.Inlines.Add(link);
p.Inlines.Add("to continue");
rtb.Blocks.Add(p);
this works fine for me.
PS
If you want to show some html in you app, you can use HTMLTextBox or HTMLViewer from http://msptoolkit.codeplex.com/
Is it possible to paste text into a Rich Text Box, while keeping the font being used in the Rich Text Box for the pasted content ?
In other words, I'd like to copy something from Word that is formated (i.e: a text that uses a font X and is underlined and in blue), and then paste it in my RichTextBox.
I would like the pasted content to have the same font as that of my RichTextBox but keep its original coloring and underlining.
Is such a thing possible ?
I use winforms.
Thanks
This is not possible out of the box. But you can do something like this:
public void SpecialPaste()
{
var helperRichTextBox = new RichTextBox();
helperRichTextBox.Paste();
for(int i=0;i<helperRichTextBox.TextLength;++i)
{
helperRichTextBox.SelectionStart = i;
helperRichTextBox.SelectionLength = 1;
helperRichTextBox.SelectionFont = new Font(richTextBox1.SelectionFont.FontFamily, richTextBox1.SelectionFont.Size,helperRichTextBox.SelectionFont.Style);
}
richTextBox1.SelectedRtf = helperRichTextBox.Rtf;
}
This changes the font of the pasted RTF to that of the character preceding the caret position at the time of the paste.
I assume that will get problematic pretty fast, if the text you paste is large(er). Additionally, this can be optimized in a way, that it sets the font only once for all characters in a row with the same base font as Hans suggests.
Update:
Here is the optimized version, that sets the font for a connected set of characters with the same original font:
public void SpecialPaste()
{
var helperRichTextBox = new RichTextBox();
helperRichTextBox.Paste();
helperRichTextBox.SelectionStart = 0;
helperRichTextBox.SelectionLength = 1;
Font lastFont = helperRichTextBox.SelectionFont;
int lastFontChange = 0;
for (int i = 0; i < helperRichTextBox.TextLength; ++i)
{
helperRichTextBox.SelectionStart = i;
helperRichTextBox.SelectionLength = 1;
if (!helperRichTextBox.SelectionFont.Equals(lastFont))
{
lastFont = helperRichTextBox.SelectionFont;
helperRichTextBox.SelectionStart = lastFontChange;
helperRichTextBox.SelectionLength = i - lastFontChange;
helperRichTextBox.SelectionFont = new Font(richTextBox1.SelectionFont.FontFamily, richTextBox1.SelectionFont.Size, helperRichTextBox.SelectionFont.Style);
lastFontChange = i;
}
}
helperRichTextBox.SelectionStart = helperRichTextBox.TextLength-1;
helperRichTextBox.SelectionLength = 1;
helperRichTextBox.SelectionFont = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size, helperRichTextBox.SelectionFont.Style);
richTextBox1.SelectedRtf = helperRichTextBox.Rtf;
}
It's pretty ugly code and I am sure it can be improved and cleaned. But it does what it should.
Clearly this won't work the way you want it if the RTF on the clipboard contains a fragment with a /font directive. Which is very likely. Filtering the RTF fragment is only practical by pasting into a helper RichTextBox. Use the SelectionFont property then copy it back to the clipboard and Paste(). Or do it directly:
int oldpos = richTextBox1.SelectionStart;
richTextBox1.SelectionLength = 0;
richTextBox1.Paste();
int newpos = richTextBox1.SelectionStart;
richTextBox1.SelectionStart = oldpos;
richTextBox1.SelectionLength = newpos - oldpos;
richTextBox1.SelectionFont = richTextBox1.Font;
richTextBox1.SelectionStart = newpos;
I know this is a bit late, but I ran into the same problem and here is my solution (hopefully this will help others):
First, handle the KeyDown event for the RichTextBox:
this.richTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RichTextBoxKeyDown);
Next, check for paste key event and re-set the clipboard text (this is where the magic happens):
private void RichTextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V)
{
try
{
Clipboard.SetText(Clipboard.GetText());
}
catch (Exception)
{
}
}
}
Explanation:
I'll start by saying that this was only tested with .NET 4.0. Assuming that none of the functions being used were altered, this will also work with older versions of .NET.
Calling Clipboard.GetText() returns the content in a plain text format (excluding RTF tags). Then we alter the text that's going to be pasted by calling Clipboard.SetText() with the plain text we fetched from Clipboard.GetText(). Now, when the event is done and is passed to the control, it will perform the paste fetching the latest text from the clipboard (our altered version). The reason it is wrapped in a try/catch block is because SetText sometimes throws an exception even though it successfully copied the text to the clipboard. You can of course use the other methods provided by Clipboard to get/set the text, this is just a basic version of the solution.
The newly pasted text will inherit the format of the cursor position, similar to manually typing into the RTB.
Unfortunately this will remove the style of the text as well (bold, coloring, etc)
Hope this helps!
Old I know; Daniel's answer works for me, but only if I replace any instance of richTextBox1.Selection and simply make references to the font and size of the entire richTextBox1. In this case, any RTF I paste will inherit the fontfamily and fontsize currently being used by richTextBox1, while preserving and RTF styling.
public void SpecialPaste()
{
var helperRichTextBox = new RichTextBox();
helperRichTextBox.Paste();
helperRichTextBox.SelectionStart = 0;
helperRichTextBox.SelectionLength = 1;
Font lastFont = helperRichTextBox.SelectionFont;
int lastFontChange = 0;
for (int i = 0; i < helperRichTextBox.TextLength; ++i)
{
helperRichTextBox.SelectionStart = i;
helperRichTextBox.SelectionLength = 1;
if (!helperRichTextBox.SelectionFont.Equals(lastFont))
{
lastFont = helperRichTextBox.SelectionFont;
helperRichTextBox.SelectionStart = lastFontChange;
helperRichTextBox.SelectionLength = i - lastFontChange;
helperRichTextBox.SelectionFont = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size, helperRichTextBox.SelectionFont.Style);
lastFontChange = i;
}
}
helperRichTextBox.SelectionStart = helperRichTextBox.TextLength-1;
helperRichTextBox.SelectionLength = 1;
helperRichTextBox.SelectionFont = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size, helperRichTextBox.SelectionFont.Style);
richTextBox1.Rtf = helperRichTextBox.Rtf;
}
I've tried to copy text from a word document and pasted it to a RichTextBox in runtime. Everything works fine. I did not adjust anything specific. Just dropped the RichTextBox onto a form and copied the formatted text from the MS Word document.