WPF RichTextBox appending coloured text - c#

I'm using the RichTextBox.AppendText function to add a string to my RichTextBox. I'd like to set this with a particular colour. How can I do this?

Just try this:
TextRange tr = new TextRange(rtb.Document.ContentEnd,­ rtb.Document.ContentEnd);
tr.Text = "textToColorize";
tr.ApplyPropertyValue(TextElement.­ForegroundProperty, Brushes.Red);

If you want, you can also make it an extension method.
public static void AppendText(this RichTextBox box, string text, string color)
{
BrushConverter bc = new BrushConverter();
TextRange tr = new TextRange(box.Document.ContentEnd, box.Document.ContentEnd);
tr.Text = text;
try
{
tr.ApplyPropertyValue(TextElement.ForegroundProperty,
bc.ConvertFromString(color));
}
catch (FormatException) { }
}
This will make it so you can just do
myRichTextBox.AppendText("My text", "CornflowerBlue");
or in hex such as
myRichTextBox.AppendText("My text", "0xffffff");
If the color string you type is invalid, it simply types it in the default color (black).
Hope this helps!

Be Aware of TextRange's Overhead
I spent a lot of time tearing my hair out, because TextRange wasn't fast enough for my use-case. This method avoids the overhead. I ran some barebones tests, and its faster by a factor of ~10 (but don't take my word for it lol, run your own tests)
Paragraph paragraph = new Paragraph();
Run run = new Run("MyText");
paragraph.Inlines.Add(run);
myRichTextBox.Document.Blocks.Add(paragraph);
Credit
Note: I think most use cases should work fine with TextRange. My use-case involved hundreds of individual appends, and that overhead stacks up.

Just a complete example which mixes original question with previous remark from Tony
var paragraph = new Paragraph();
var run = new Run(message)
{
Foreground = someBrush
};
paragraph.Inlines.Add(run);
myRichTextBox.Document.Blocks.Add(paragraph);
Now, it is fast and coloured :)
Note that (unlike the TextRange solution) this solution also solved me a line break issue occurring at the first line of the RichTextBox.

I ended up synthesising Omni and Kishores' answers and creating an extension method as so:
public static void AppendText(this System.Windows.Controls.RichTextBox box, string text, SolidColorBrush brush)
{
TextRange tr = new TextRange(box.Document.ContentEnd, box.Document.ContentEnd);
tr.Text = text;
tr.ApplyPropertyValue(TextElement.ForegroundProperty, brush);
}
Which can be called as so:
MyTextBox.AppendText("Some Text\n", Brushes.Green);

the above single line answer:-
myRichTextBox.AppendText("items", "CornflowerBlue")
is not working.The correct way it should be writen is (i am using VS 2017) :-
Dim text1 As New TextRange(myRichTextBox.Document.ContentStart, myRichTextBox.Document.ContentEnd)
myRichTextBox.AppendText("items")
text1.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.CornflowerBlue)

Related

RTF Color Table looping through all colors

I have a RichTextBox. I have added RTF formatting (mainly a color table) to this RichTextBox. When I first Append text to it, it loops through all* the colors of the color table.
*It starts with applying color0, then color1, then color2, etc until all the colors in the color table have been applied OR if the text that is being output has one of those colors already - in that case it stops this "looping" and continues as intended. See screenshot for example.
Here is the code:
private void populateColorCodeDictionary() {
startRTFString = #"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0;}}" +
#"{\colortbl
;
\red0\green0\blue0;
\red170\green0\blue0;
\red0\green170\blue0;
\red128\green128\blue0;
\red0\green0\blue128;
\red128\green0\blue128;
\red0\green128\blue128;
\red127\green127\blue127;
\red85\green85\blue85;
\red255\green0\blue0;
\red0\green255\blue0;
\red255\green255\blue0;
\red0\green0\blue255;
\red255\green0\blue255;
\red0\green255\blue255;
\red255\green255\blue255;
}";
colorCodeDictionary.Add("\x1b[0;30m", #"\cf1");
colorCodeDictionary.Add("\x1b[0;31m", #"\cf2");
colorCodeDictionary.Add("\x1b[0;32m", #"\cf3");
colorCodeDictionary.Add("\x1b[0;33m", #"\cf4");
colorCodeDictionary.Add("\x1b[0;34m", #"\cf5");
colorCodeDictionary.Add("\x1b[0;35m", #"\cf6");
colorCodeDictionary.Add("\x1b[0;36m", #"\cf7");
colorCodeDictionary.Add("\x1b[0;37m", #"\cf8");
colorCodeDictionary.Add("\x1b[1;30m", #"\cf9");
colorCodeDictionary.Add("\x1b[1;31m", #"\cf10");
colorCodeDictionary.Add("\x1b[1;32m", #"\cf11");
colorCodeDictionary.Add("\x1b[1;33m", #"\cf12");
colorCodeDictionary.Add("\x1b[1;34m", #"\cf13");
colorCodeDictionary.Add("\x1b[1;35m", #"\cf14");
colorCodeDictionary.Add("\x1b[1;36m", #"\cf15");
colorCodeDictionary.Add("\x1b[1;37m", #"\cf16");
/*
\x1b[0;30m = cf1 = black
\x1b[0;31m = cf2 = red
\x1b[0;32m = cf3 = green
\x1b[0;33m = cf4 = brown
\x1b[0;34m = cf5 = blue
\x1b[0;35m = cf6 = purple
\x1b[0;36m = cf7 = cyan
\x1b[0;37m = cf8 = gray
\x1b[1;30m = cf9 = darkGray
\x1b[1;31m = cf10 = light Red
\x1b[1;32m = cf11 = light green
\x1b[1;33m = cf12 = yellow
\x1b[1;34m = cf13 = light blue
\x1b[1;35m = cf14 = indigo
\x1b[1;36m = cf15 = light cyan
\x1b[1;37m = cf16 = white
*/
}
The above method sets up the variables. The most interesting part is the startRTFString variable.
private void updateOutputWindow(string text) {
string newText = string.Empty;
if (InvokeRequired) {
Invoke(new MethodInvoker(delegate () {
updateOutputWindow(text);
}));
}
else {
newText = startRTFString;
newText += rtb_outputWindow.Rtf;
newText += replaceAnsiColorCodes(text);
rtb_outputWindow.Rtf = newText;
}
}
The above method outputs text to the RichTextBox.
private string replaceAnsiColorCodes(string inData) {
string returnString = inData;
foreach (KeyValuePair<string, string> entry in colorCodeDictionary) {
returnString = returnString.Replace(entry.Key, entry.Value);
}
returnString = returnString.Replace("\r", #"\line"); //Newline
returnString = returnString.Replace("\x1b[0;1m", ""); //Bold
returnString = returnString.Replace("\x1b[0m", #"\cf16 "); //Reset
return returnString;
}
The above method converts ANSI codes into RTF color codes. (As well as newline and bold. I have chosen to set bold to be nothing, as of now.)
for (int i = 0; i < 15; i++) {
updateOutputWindow("\x1b[0mline" + i.ToString());
}
The above is just a little loop that I run as soon as the application has started. It is for testing purposes and can be seen in the following screenshot:
Above is a screenshot of the issue. As you can see, the first 15 lines of text all have different colors. They should all have the same, default, color (in my case white.) After the "looping" is done, it continues to work as intended; applying the correct color to the text.
It should be noted that the line above the first (line0) is empty. The color table starts with black, so line0 should be black colored, and line1 should be dark red. Don't know why it's not following it's own rules.
Here is another test I ran:
for (int i = 0; i < 15; i++) {
updateOutputWindow("\x1b[0;34mline" + i.ToString()); //This time I changed the color to be dark blue
}
As you can see: the "looping" happened until it ran into the same color, and it kept going with that color.
I have no idea why this happens or what causes it. My guess is that I have messed up the RTF "script" somehow. Does anyone know of a solution? (I don't want ANY of this color table looping to happen. I want it to output the default color, unless there is a color code present - in that case I want that color to be presented.)
EDIT:
I added this bit of code to the method updateOutputWindow MessageBox.Show(newText);. Below is the screenshot of the result:
As you can see from the screenshot above the RichTextBox has some kind of "default RTF code" already in-place. -This default code gets added ontop of my "custom RTF code". It doesn't seem to interfere with the color table, though. (Unless this is what is casing the issue at hand, in that case it most certainly is interfering, but in a very specific, one-time way.)
EDIT #2: If I continue to run this method over and over again, the RTF-code doesn't get added for each additional run. It gets added once (at the very top) and then no more. I think that is a good thing, and I believe it is caused by how RichTextBoxes natively handles RTF code.
You are converting terminal codes (or as you call them ANSI codes) to RTF format. Text that you give to updateOutputWindow contains \x1b[0;34m, but in updateOutputWindow there is no Replace line for that terminal code. You have something similar, but not exactly that. As a result a terminal code is now part of RTF, so who knows what happens. Must deal with all terminal codes (replace or remove).
Also, the following code seems strange to me:
newText = startRTFString;
newText += rtb_outputWindow.Rtf;
newText += replaceAnsiColorCodes(text);
rtb_outputWindow.Rtf = newText;
It seems that rtb_outputWindow.Rtf will grow with every function call, adding new startRTFString every time. Instead of the code above I propose the following (myStuff is a property, like startRTFString):
myStuff = myStuff + replaceAnsiColorCodes(text);
rtb_outputWindow.Rtf = startRTFString + myStuff;

extract lines from a flowdocument in a richtextbox starting with specific words WPF

I'm very new to wpf and I would like to make a text analysing tool.
I already know how to import text into rich textbox and format it properly, but now I want to run a method that extracts all lines in the flowdocument that start with INT or EXT and place them in a listbox. It seems to be quite easier to do this in winforms than in WPF.
Is there someone who can guide me with this?
I wish I could already provide some code but the flowdocument is new to me as is wpf.
I have written a code snippet to collect the lines that begin with INT or EXT.
I am sure the code is not optimal, because i am not practised with RichTextBox, but i think it is very easy to understand.
private List<string> CollectLines()
{
TextRange textRange = new TextRange(
// TextPointer to the start of content in the RichTextBox.
TestRichTextBox.Document.ContentStart,
// TextPointer to the end of content in the RichTextBox.
TestRichTextBox.Document.ContentEnd);
// The Text property on a TextRange object returns a string
// representing the plain text content of the TextRange.
var text = textRange.Text;
List<string> resultList = new List<string>();
// Collect all line that begin with INT or EXT
// Or use .Contains if the line could begin with a tab (\t), spacing or whatever
using (StringReader sr = new StringReader(text))
{
var line = sr.ReadLine();
while (line != null)
{
if (line.StartsWith("INT") || line.StartsWith("EXT"))
{
resultList.Add(line);
}
line = sr.ReadLine();
}
}
return resultList;
}
Maybe you can find out how you can put the list into a listbox yourself :)

How to extend TextBox's properties to create a RichTextBox?

My Question is to how to extend a TextBox such that it may start behaving like RichTextBox?
There can be various properties that RichTextBox may add: appearance mainly.
Should I use this kind of method where I extend the TextBox class and create a basic TextBox which would contain several other textboxes which would behave like a big container node containing small specialized nodes?
For starters, to have texts with alternate color after '+', I ve used this way:
class CustomTextBox : TextBox
{
List<TextBox> _textboxes = new List<TextBox>();
string _Text="";
List<Color> colorlist = new List<Color>();
public override string Text
{
get{return this._Text;}
set{this._Text = value;}
}
public CustomTextBox()
{
foreach(Color color in (Color[]) Enum.GetValues(typeOf(Color)))
{
colorlist.add(color);
}
this.KeyUp+= new KeyUpEventHandler(TextChangedCheck);
}
int i=0;
private void TextChangedCheck(object sender, KeyUpEventArgs e)
{
if(e.KeyData == Keys.Add)
{
TextBox Temp = new TextBox();
Temp.Text = this.Text;
this.Text = "";
Temp.ForeColor = colorlist[i];
i++;
this._textboxes.Add(Temp);
this.Controls.Add(_textboxes[i]);
e.Handled = true;
}
}
}
EDIT:
The MAIN purpose of this question is to extend a TextBox using its own properties to have a RTB like behavior and not using Graphics or related.
I'd have to guess what you really want to achieve here.
But if you want a Control with formatted text (FT) and really really really don't want a RichTextBox, I think you shouldn't create a new, embedded TextBox for every piece of FT.
Instead you should write the FT yourself, maybe on a panel with DrawString and use only one Textbox for text entry. Interesting project, really, once one accepts the challenge. Of course you must think your format through and also consider all sorts of interface questions..
Last week I have avoided a RTF for a tiny help system by using a ListView; it formats by line only, using the first character to indicate the format from a list of a dozen or so.. Works fine, but only one format per line.

How to make a part of Stringbuilder content bold?

StringBuilder sb = new StringBuilder();
sb.Append(
string.Format("{0} |{1} ", Name, Value)
);
Display.Text = sb.ToString(); // Display is a WP7 TextBlock control
I want to make "Name" as bold. Is it possible to do that ?
ChrisF offers the RichTextBox as a solution but its less well known that simple font variation is acheivable with the simple TextBlock:-
myTextBlock.Inlines.Add(new Run() { Text = "Hello " });
myTextBlock.Inlines.Add(new Run() { Text = "World", FontWeight= FontWeights.Bold });
A StringBuilder only contains character data, not formatting. You can't, basically. Unless you are actually generating html or rtf etc.
In the same way that notepad.exe doesn't have bold/italics/etc.
I'm not a WP7 expert, but maybe there is a different control you can use here, more aimed at formatted text.
You'll need to put the text into a RichTextBox and have the name as a separate Run in the Paragraph as in this example from the MSDN:
// Create a Run of plain text and some bold text.
Run myRun1 = new Run();
myRun1.Text = "A RichTextBox with ";
Bold myBold = new Bold();
myBold.Inlines.Add("initial content ");
Run myRun2 = new Run();
myRun2.Text = "in it.";
// Create a paragraph and add the Run and Bold to it.
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add(myRun1);
myParagraph.Inlines.Add(myBold);
myParagraph.Inlines.Add(myRun2);
// Add the paragraph to the RichTextBox.
MyRTB.Blocks.Add(myParagraph);

C# Winforms bold treeview node doesn't show whole text

I'm using the following code to make my treenodes bold:
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
foreach (QuestionnaireBuilder_Category cat in categories)
{
TreeNode node = new TreeNode();
node.Text = cat.Description;
node.Name = cat.Id.ToString();
node.NodeFont = font;
tvQuestionSequence.Nodes.Add(node);
}
But the text of the bold nodes is not displayed correctly. The last letter(s) are not shown. How come? And how to solve this problem?
I found this Post when searching through the web because I am facing the exact same problem.
However, appending a white space to the end of the node was not an option, and I found an alternative way that seems to fix the issue.
After setting my node font Bold, all I need to do is reset the node text with the same value.
Here is the Code Sample:
Font boldFont = new Font(treeview.Font, FontStyle.Bold);
node.NodeFont = boldFont;
node.Text = node.Text;
It seems that the node is redrawn after changing the text, which is exactly what I wanted in the first place.
I've found that this is a Windows issue. A workaround for this problem is this:
In the form constructor set the font of the treeview to bold. When adding nodes which must not be bold, change the font to regular:
// Constructor of your form
public Form()
{
InitializeComponent();
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
tvQuestionSequence.Font = font;
}
// Add regular nodes (not bold)
Font font = new Font(tvQuestionSequence.Font, FontStyle.Regular);
TreeNode treeNode = new TreeNode();
treeNode.Text = "Foo";
treeNode.NodeFont = font;
TreeNode parent = tvQuestionSequence.Nodes.Find("parent", true);
parent.Nodes.Add(treeNode);
Simply use treeView.BeginUpdate() before you bold the node then treeView.EndUpdate() after you've bolded the node.
This is a known Windows bug.
The simple solution is just to append an extra space character at the end of your strings. The space character will not be visible, but it will increase the number of pixels needed to draw the string, so the entire string will be visible.
This is all not helping for me.
What DID the trick is making the font a little bigger and bold at DESIGN time.
(In the Properties window)
So make sure you define the treeview with big enough font, then later you can add nodes with smaller font. They will fit.
I do agree with the solution provided. I just want to add to it to shed a little more light on what the problem is.
The treeview has its own font which determines the width of items at the root level. That compensates for the fact that there is only an item height property available and no item width property.
The solution to your problem is to determine what the font of your root node should be, then set the tree to that same font. You can do that at design time also.
Hope that helps someone.
A workaround for this problem is this:
Set the defaul font of treeview to bold in the properties.
And chnage to not bold when you need.
I do the following, I set the DrawNode Event to call, it sets the node to bold and removes the highlighted colour.
You can set any colour you like using the first parameter of the e.Graphics.FillRectangle function.
private void SetNodeBoldWhenSelected(object sender, DrawTreeNodeEventArgs e)
{
if (e.Node == null) return;
var font = e.Node.NodeFont ?? e.Node.TreeView.Font;
if (e.Node.IsSelected)
{
font = new Font(font, FontStyle.Bold);
}
var bounds = new Rectangle( e.Bounds.X,e.Bounds.Y,e.Bounds.Width+20,e.Bounds.Height);
e.Graphics.FillRectangle(SystemBrushes.ControlDarkDark, bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, font, bounds, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
}
Now when I select a node I get 20 pixels more space, for my font, this works well, one can calculate the "real" size needed but there is no specification stating it needs to do this but you can use Graphics.MeasureString if you feel you need to do that.
Very easy and works fine
treeView1.SelectedNode.NodeFont = new System.Drawing.Font(treeView1.SelectedNode.TreeView.Font, treeView1.SelectedNode.TreeView.Font.Style | FontStyle.Bold);
this.treeView1.SelectedNode.Text += string.Empty;
I realize this is an old thread and it may have been answered. I just ran across this problem as I'm learning to use TreeViews. What worked for me was changing the font size for the entire TreeView to the same size, or bigger than the font of the level you want to bold. The default font size is 8.something. I changed mine to 10, which was the size I wanted my nodes, and the truncating was gone.
What worked for me: Hooking into the load event in the Control's constructor and tweaking the node as explained in BlunT's answer.
public MyControl()
{
InitializeComponent();
_head = new TreeNode();
this.Load += (s, e) =>
{
trvParts.Nodes.Clear();
_head.NodeFont = new Font(trvParts.Font, FontStyle.Bold);
trvParts.Nodes.Add(_head);
_head.Text = "Node Name";
};
}
It's in vb.Net however the solution to re-enter the value of the TEXT field gets around this nicely. As in:
With myNode
Dim myText As String = .Text 'capture the text
.NodeFont = New Font(<name of your treeview>.Font, FontStyle.Bold)
.Text = myText 'reset the text to the original value
End With
Based on MSDN Library, try change your code to:
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
foreach (QuestionnaireBuilder_Category cat in categories)
{
TreeNode node = new TreeNode();
node.Text = cat.Description;
node.Name = cat.Id.ToString();
node.NodeFont = font;
tvQuestionSequence.BeginUpdate(); //added newline here <--
tvQuestionSequence.Nodes.Add(node);
tvQuestionSequence.EndUpdate(); //added newline here <--
}
It work for me

Categories

Resources