I'm trying to color parts of a string to be appended to a RichTextBox. I have a string built from different strings.
string temp = "[" + DateTime.Now.ToShortTimeString() + "] " +
userid + " " + message + Environment.NewLine;
This is what the message would look like once it is constructed.
[9:23pm] User: my message here.
I want everything within and including the brackets [9:23] to be one color, 'user' to be another color and the message to be another color. Then I'd like the string appended to my RichTextBox.
How can I accomplish this?
Here is an extension method that overloads the AppendText method with a color parameter:
public static class RichTextBoxExtensions
{
public static void AppendText(this RichTextBox box, string text, Color color)
{
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.SelectionColor = color;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
}
}
And this is how you would use it:
var userid = "USER0001";
var message = "Access denied";
var box = new RichTextBox
{
Dock = DockStyle.Fill,
Font = new Font("Courier New", 10)
};
box.AppendText("[" + DateTime.Now.ToShortTimeString() + "]", Color.Red);
box.AppendText(" ");
box.AppendText(userid, Color.Green);
box.AppendText(": ");
box.AppendText(message, Color.Blue);
box.AppendText(Environment.NewLine);
new Form {Controls = {box}}.ShowDialog();
Note that you may notice some flickering if you're outputting a lot of messages. See this C# Corner article for ideas on how to reduce RichTextBox flicker.
I have expanded the method with font as a parameter:
public static void AppendText(this RichTextBox box, string text, Color color, Font font)
{
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.SelectionColor = color;
box.SelectionFont = font;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
}
This is the modified version that I put in my code (I'm using .Net 4.5) but I think it should work on 4.0 too.
public void AppendText(string text, Color color, bool addNewLine = false)
{
box.SuspendLayout();
box.SelectionColor = color;
box.AppendText(addNewLine
? $"{text}{Environment.NewLine}"
: text);
box.ScrollToCaret();
box.ResumeLayout();
}
Differences with original one:
Possibility to add text to a new line or simply append it
No need to change selection, it works the same
Inserted ScrollToCaret to force autoscroll
Added SuspendLayout/ResumeLayout calls for better performance
EDIT : sorry this is a WPF answer
I think modifying a "selected text" in a RichTextBox isn't the right way to add colored text.
So here a method to add a "color block" :
Run run = new Run("This is my text");
run.Foreground = new SolidColorBrush(Colors.Red); // My Color
Paragraph paragraph = new Paragraph(run);
MyRichTextBlock.Document.Blocks.Add(paragraph);
From MSDN :
The Blocks property is the content property of RichTextBox. It is a
collection of Paragraph elements. Content in each Paragraph element
can contain the following elements:
Inline
InlineUIContainer
Run
Span
Bold
Hyperlink
Italic
Underline
LineBreak
So I think you have to split your string depending on parts color, and create as many Run objects as needed.
It`s work for me! I hope it will be useful to you!
public static RichTextBox RichTextBoxChangeWordColor(ref RichTextBox rtb, string startWord, string endWord, Color color)
{
rtb.SuspendLayout();
Point scroll = rtb.AutoScrollOffset;
int slct = rtb.SelectionIndent;
int ss = rtb.SelectionStart;
List<Point> ls = GetAllWordsIndecesBetween(rtb.Text, startWord, endWord, true);
foreach (var item in ls)
{
rtb.SelectionStart = item.X;
rtb.SelectionLength = item.Y - item.X;
rtb.SelectionColor = color;
}
rtb.SelectionStart = ss;
rtb.SelectionIndent = slct;
rtb.AutoScrollOffset = scroll;
rtb.ResumeLayout(true);
return rtb;
}
public static List<Point> GetAllWordsIndecesBetween(string intoText, string fromThis, string toThis,bool withSigns = true)
{
List<Point> result = new List<Point>();
Stack<int> stack = new Stack<int>();
bool start = false;
for (int i = 0; i < intoText.Length; i++)
{
string ssubstr = intoText.Substring(i);
if (ssubstr.StartsWith(fromThis) && ((fromThis == toThis && !start) || !ssubstr.StartsWith(toThis)))
{
if (!withSigns) i += fromThis.Length;
start = true;
stack.Push(i);
}
else if (ssubstr.StartsWith(toThis) )
{
if (withSigns) i += toThis.Length;
start = false;
if (stack.Count > 0)
{
int startindex = stack.Pop();
result.Add(new Point(startindex,i));
}
}
}
return result;
}
Selecting text as said from somebody, may the selection appear momentarily.
In Windows Forms applications there is no other solutions for the problem, but today I found a bad, working, way to solve: you can put a PictureBox in overlapping to the RichtextBox with the screenshot of if, during the selection and the changing color or font, making it after reappear all, when the operation is complete.
Code is here...
//The PictureBox has to be invisible before this, at creation
//tb variable is your RichTextBox
//inputPreview variable is your PictureBox
using (Graphics g = inputPreview.CreateGraphics())
{
Point loc = tb.PointToScreen(new Point(0, 0));
g.CopyFromScreen(loc, loc, tb.Size);
Point pt = tb.GetPositionFromCharIndex(tb.TextLength);
g.FillRectangle(new SolidBrush(Color.Red), new Rectangle(pt.X, 0, 100, tb.Height));
}
inputPreview.Invalidate();
inputPreview.Show();
//Your code here (example: tb.Select(...); tb.SelectionColor = ...;)
inputPreview.Hide();
Better is to use WPF; this solution isn't perfect, but for Winform it works.
I created this Function after researching on the internet since I wanted to print an XML string when you select a row from a data grid view.
static void HighlightPhrase(RichTextBox box, string StartTag, string EndTag, string ControlTag, Color color1, Color color2)
{
int pos = box.SelectionStart;
string s = box.Text;
for (int ix = 0; ; )
{
int jx = s.IndexOf(StartTag, ix, StringComparison.CurrentCultureIgnoreCase);
if (jx < 0) break;
int ex = s.IndexOf(EndTag, ix, StringComparison.CurrentCultureIgnoreCase);
box.SelectionStart = jx;
box.SelectionLength = ex - jx + 1;
box.SelectionColor = color1;
int bx = s.IndexOf(ControlTag, ix, StringComparison.CurrentCultureIgnoreCase);
int bxtest = s.IndexOf(StartTag, (ex + 1), StringComparison.CurrentCultureIgnoreCase);
if (bx == bxtest)
{
box.SelectionStart = ex + 1;
box.SelectionLength = bx - ex + 1;
box.SelectionColor = color2;
}
ix = ex + 1;
}
box.SelectionStart = pos;
box.SelectionLength = 0;
}
and this is how you call it
HighlightPhrase(richTextBox1, "<", ">","</", Color.Red, Color.Black);
private void Log(string s , Color? c = null)
{
richTextBox.SelectionStart = richTextBox.TextLength;
richTextBox.SelectionLength = 0;
richTextBox.SelectionColor = c ?? Color.Black;
richTextBox.AppendText((richTextBox.Lines.Count() == 0 ? "" : Environment.NewLine) + DateTime.Now + "\t" + s);
richTextBox.SelectionColor = Color.Black;
}
Using Selection in WPF, aggregating from several other answers, no other code is required (except Severity enum and GetSeverityColor function)
public void Log(string msg, Severity severity = Severity.Info)
{
string ts = "[" + DateTime.Now.ToString("HH:mm:ss") + "] ";
string msg2 = ts + msg + "\n";
richTextBox.AppendText(msg2);
if (severity > Severity.Info)
{
int nlcount = msg2.ToCharArray().Count(a => a == '\n');
int len = msg2.Length + 3 * (nlcount)+2; //newlines are longer, this formula works fine
TextPointer myTextPointer1 = richTextBox.Document.ContentEnd.GetPositionAtOffset(-len);
TextPointer myTextPointer2 = richTextBox.Document.ContentEnd.GetPositionAtOffset(-1);
richTextBox.Selection.Select(myTextPointer1,myTextPointer2);
SolidColorBrush scb = new SolidColorBrush(GetSeverityColor(severity));
richTextBox.Selection.ApplyPropertyValue(TextElement.BackgroundProperty, scb);
}
richTextBox.ScrollToEnd();
}
I prepared a little helper for the RichTextBox control which makes it very easy to generate colored text on the screen:
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace Common.Helpers
{
public class ColouredText
{
public string Text;
public Color Foreground;
public Color Background;
public ColouredText(string text, Color foreground, Color background)
{
Text = text;
Foreground = foreground;
Background = background;
}
public ColouredText(string text, Color foreground) : this(text, foreground, Color.Transparent) { }
public ColouredText(string text) : this(text, Color.Transparent, Color.Transparent) { }
}
public static class RichTextBoxHelper
{
private static RichTextBox _AppendText(RichTextBox box, string text, Color foreColor, Color backColor)
{
if (string.IsNullOrEmpty(text)) return box;
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.SelectionColor = foreColor;
box.SelectionBackColor = backColor;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
return box;
}
private static void _UpdateText(RichTextBox box, IEnumerable<ColouredText> newTextWithColors)
{
box.Text = "";
foreach (var text in newTextWithColors)
{
var foreColor = text.Foreground; if (foreColor == Color.Transparent) foreColor = box.ForeColor;
var backColor = text.Background; if (backColor == Color.Transparent) backColor = box.BackColor;
_AppendText(box, text.Text, foreColor, backColor);
}
}
public static void UpdateText(this RichTextBox richTextbox, IEnumerable<ColouredText> text)
{
if (richTextbox.InvokeRequired) richTextbox.Invoke((MethodInvoker)(() => { _UpdateText(richTextbox, text); }));
else _UpdateText(richTextbox, text);
}
public static void UpdateText(this RichTextBox richTextbox, ColouredText text)
{
var list = new List<ColouredText>() { text };
if (richTextbox.InvokeRequired) richTextbox.Invoke((MethodInvoker)(() => { _UpdateText(richTextbox, list); }));
else _UpdateText(richTextbox, list);
}
}
}
and now you can use:
var text = new List<ColouredText>()
{
new ColouredText($"text#1 ", Color.Black),
new ColouredText($"text#2 ", Color.Red, Color.Yellow),
new ColouredText($" "),
new ColouredText($"text#2 ", Color.White, Color.Black)
};
richTextBox1.UpdateText(text);
or simpler usage for single-line text:
richTextBox1.UpdateText(new ColouredText($"warning message", Color.Yellow, Color.Red));
I have my programme here, where you can see I have a string called "S", and a void Get Temperature. At the bottom where keypresses are processed, it has an else If statement with ENTER. I want it so that when you press enter, it updates the string (s) to whatever you have typed, and then load it into the "SetAddress" field. How would I go about this?
import com.temboo.core.*;
import com.temboo.Library.Yahoo.Weather.*;
import ddf.minim.*;
AudioPlayer player;
Minim minim;
PImage bg;
String myText;
PFont Bold;
PFont Thin;
TembooSession session = new TembooSession("goldsmiths-c", "myFirstApp", "CNgLbwqnqzGdsnk6wHXPfAnQNSmV0Fmr");
String s = "Enter Location";
int prev = frameCount;
//KeyPressed KeyPressed = new KeyPressed();
void setup() {
size(960, 540);
bg = loadImage("mountains.jpg");
minim = new Minim(this);
player = minim.loadFile("song.mp3");
player.play();
player.loop();
runGetTemperatureChoreo();
Bold = createFont ("TTFirsBlackItalic.otf", height);
Thin = createFont ("TTFirsThin.otf", height);
frameRate (30);
}
void draw() {
background(bg);
fill (0);
textFont (Bold);
textSize (48);
fill(255, 255, 255);
text(myText, 10, 390);
fill(255, 255, 255);
textFont (Thin);
textSize (48);
text(s, 10, 500);
print(mouseY);
}
void runGetTemperatureChoreo() {
GetTemperature getTemperatureChoreo = new GetTemperature(session);
getTemperatureChoreo.setAddress(s);
getTemperatureChoreo.setUnits("c");
GetTemperatureResultSet getTemperatureResults = getTemperatureChoreo.run();
myText = (s) + (getTemperatureResults.getTemperature() + ("°c"));
print(getTemperatureResults.getTemperature());
}
void keyPressed()
{
if (keyPressed && prev <= frameCount-10) { //If a key is being pressed, and the security/delay is fine with it
prev = frameCount; //re-Init the clock
if (keyCode == BACKSPACE) { //Delete a char!
if (s.length() > 0) {
s = s.substring(0, s.length()-1);
}
} else if (keyCode == DELETE) {
s = "";
} else if (keyCode == ENTER && s.length() != 0) {
} else if (keyCode != SHIFT && keyCode != CONTROL && keyCode != ALT && s.length() < 20) { //It's an ok char, add it to the String
s += key;
}
}
}
Well, it looks like you're storing what the user types in the s variable. You then have this part of your if statement:
else if (keyCode == ENTER && s.length() != 0) {
}
That else if will be entered whenever the user presses enter after typing something. So you just have to put whatever code you want inside the body of that statement:
else if (keyCode == ENTER && s.length() != 0) {
getTemperatureChoreo.setAddress(s);
}
I might not be fully understanding your code, so this is just an example. But the basic idea is the same: to do something when the user presses enter, just put the code you want to happen inside this else if block.
In the future, please try to post an MCVE instead of your entire sketch. For example, your question has nothing to do with minim, so you can get rid of all of that code. Start over with a blank sketch and only add enough code so we can copy and paste it to run it ourselves to see where you're stuck. Good luck.
here is my TextBox written in C# with Key Down event handler
private void TextBox_KeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
//ONLY ACCEPTS NUMBERS
char c = Convert.ToChar(e.Key);
if (!c.Equals('0') && !c.Equals('1') && !c.Equals('2') && !c.Equals('3') && !c.Equals('4') &&
!c.Equals('5') && !c.Equals('6') && !c.Equals('7') && !c.Equals('8') && !c.Equals('9'))
{
e.Handled = true;
}
}
it does works preventing letters from a to z. However, if I enter symbols like !##$%^&*()_+, it stills accept them. What am I missing?
You can use Char.IsDigit
e. Handled = !Char.IsDigit(c);
But this won't help you much in case of copy\pasting.
Also check related question on the right. For example Create WPF TextBox that accepts only numbers
UPDATE
For letters only try
e.Handled = Char.IsLetter(c);
There is no reliable way to handle this on the key down or key up events because for some odd reason shift 4 which is the $ sign returns 4 not the key value.
The best workaround is to catch the value before it's used and check it is numeric then alert the user.
var IsNumeric = new System.Text.RegularExpressions.Regex("^[0-9]*$");
if (!IsNumeric.IsMatch(edtPort.Text))
{
showMessage("Port number must be number", "Input Error");
return;
}
In no way ideal but better that trying to teach your users that the dollar sign is now a number!
Try this code. But sometimes you see char that you pressed and it is removed immediately. Not the best solution but sufficient
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null)
return;
if (textBox.Text.Length == 0) return;
var text = textBox.Text;
int result;
var isValid = int.TryParse(text, out result);
if (isValid) return;
var selectionStart = textBox.SelectionStart;
var resultString = new string(text.Where(char.IsDigit).ToArray());
var lengthDiff = textBox.Text.Length - resultString.Length;
textBox.Text = resultString;
textBox.SelectionStart = selectionStart - lengthDiff;
}
this might help, this is what I used.
private void txtBoxBA_KeyDown(object sender, KeyRoutedEventArgs e)
{
// only allow 0-9 and "."
e.Handled = !((e.Key.GetHashCode() >= 48 && e.Key.GetHashCode() <= 57));
// check if "." is already there in box.
if (e.Key.GetHashCode() == 190)
e.Handled = (sender as TextBox).Text.Contains(".");
}
I have a Radgrid who has Dynamic Databound fields which comes from a database query and each time they are different. Now when this data is displayed on the GRID, i want to change the value of Cells where its 0 to " " or "."
Thanks
You need to hook into the CellFormatting event. Here you can check the value of the cell and change it as required.
Something along the lines of:
Private Sub RadGrid_CellFormatting(sender As Object, e As CellFormattingEventArgs) Handles RadGrid.CellFormatting
if e.CellElement.RowInfo.Cells("ZeroColumn").Value = "0" then
e.CellElement.RowInfo.Cells("ZeroColumn").Value = "."
end if
End Sub
try
{
if (e.Row.RowType == DataControlRowType.Header)
{
e.Row.Cells[e.Row.Cells.Count - 1].Visible = false;
}
if (e.Row.RowType == DataControlRowType.DataRow)
{
for (int i = 3; i < e.Row.Cells.Count; ++i)
{
TextBox tb = new TextBox();
tb.ID = "txtRow" + e.Row.RowIndex.ToString() + "Column" + i.ToString();
tb.Width = 50;
//if (Convert.ToBoolean(e.Row.Cells[i].Text) == false)
//tb.Text = "0";
//tb.Text = e.Row.Cells[i].Text;
e.Row.Cells[i].Controls.Add(tb);
// e.Row.Cells[i].Enabled = Convert.ToBoolean(e.Row.Cells[i].Text);
}
e.Row.Cells[e.Row.Cells.Count - 1].Visible = false;
}
}
catch (Exception ex)
{ }
I have a datagrid and its rows are colored based on certain condtions..There is a vertical scrollbar for my datagrid.. If the user scrolls the datagrid the index of the colored rows r getting changed.. For eg: if 2 row is colored if the user scrolls down and comes up the index of the colored row is getting messed up...
here is the code....
dggeneralconfiguration.LoadingRow += new EventHandler<DataGridRowEventArgs>(grid1_LoadingRow);
dggeneralconfiguration.UnloadingRow += new EventHandler<DataGridRowEventArgs>(grid1_UnloadingRow);
void grid1_LoadingRow(object sender, DataGridRowEventArgs e)
{
ShowGeneralGrid c = e.Row.DataContext as ShowGeneralGrid;
if (c.Status == false)
{
if (e.Row != null)
{
e.Row.Background = new SolidColorBrush(Colors.Red);
//e.Row.Background = new SolidColorBrush(Colors.Transparent);
}
}
}
void grid1_UnloadingRow(object sender, DataGridRowEventArgs e)
{
ShowGeneralGrid c = e.Row.DataContext as ShowGeneralGrid;
if (c.Status == false)
{
if (e.Row != null)
{
e.Row.Background = new SolidColorBrush(Colors.Red);
//e.Row.Background = new SolidColorBrush(Colors.Transparent);
}
}
}
Better late than never. ;-) . I too had similar kind of problem.. try assigning row background to null on unloading row event.
private void dg_UnloadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Background = null;
}