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));
This is the code i have written to dynamically generate Controls:
private void GenCtrl(string type, int typeid,string sql)
{
string id = Convert.ToString(typeid);
if (type == "TextBox")
{// created for TextBox
}
else if (type == "ComboBox")
{
ComboBoxEdit cb = new ComboBoxEdit();
cb.Name = "cb" + id;
// this is to set unique id if more than 1 combobox created.
cb.Height = 20;
cb.Width = 100;
cb.Margin = new Thickness(2, 2, 0, 0);
cb.HorizontalAlignment = HorizontalAlignment.Left;
cb.Text = "Show All";
stctrl.Children.Add(cb);
ComboBoxEdit cbx = (ComboBoxEdit)cb.FindName(cb.Name);
cb.DisplayMember = "Name";
if(filsql !=null)
ServRef.GetComboBoxlistAsync(sql); //this would retrieve the list and attach to the combobox.
//ctlst.Add(cb.Name, type);
}
}
//WCF Service reference
ServRef.GetComboBoxlistCompleted += new EventHandler<GetComboBoxlistCompletedEventArgs>(ServRef_GetComboBoxlistCompleted);
void ServRef_GetComboBoxlistCompleted(object sender, GetComboBoxlistCompletedEventArgs e)
{
cb.ItemsSource = e.Result;
cb.SelectedIndex = 0;
}
The problem i am facing while binding two combobox is created ex : cb1,cb2( dynamically created)only the control cb2 combox which is the latest get activated at ServRef_GetComboBoxlistCompleted method. i am unable to bind the value for the first control(combobox).
if my question is not clear let me know.
i was able to find using FindName
ComboBoxEdit cbx = (ComboBoxEdit)stackctrl.FindName(cb.Name);
In VB .NET I have 3 characters which are added to a DataGridView cell depending on some calculations.
They are rank change arrows and work fine, but I want the up arrow to be green and the down arrow to be red.
Dim strup As String = "▲"
Dim strdown As String = "▼"
Dim strsame As String = "▬"
So in the cell a change of negative three will look like ▼3 and plus 3 will look like ▲3 where the text and symbol are different colors.
How can I change the color of the first character in DataGridView cell?
There is no easy way to do this if you have anything more than just the character in question in the cell (you would need to do some form of custom painting).
If you only have those characters then this is very easy with the CellFormatting event:
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
e.CellStyle.Font = new Font("Arial Unicode MS", 12);
if (dataGridView1.Columns[e.ColumnIndex].Name == "CorrectColumnName")
{
if (e.Value == "▲")
e.CellStyle.ForeColor = Color.Green;
else if (e.Value == "▼")
e.CellStyle.ForeColor = Color.Red;
else
e.CellStyle.ForeColor = Color.Black;
}
}
If you do want different colors within the same cell then something like the following code is required (this handles the CellPainting event):
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex == -1 || e.RowIndex == -1)
return;
if (dataGridView1.Columns[e.ColumnIndex].Name == "CorrectColumnName")
{
e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.ContentForeground);
if (e.FormattedValue.ToString().StartsWith("▲", StringComparison.InvariantCulture))
{
RenderCellText(Color.Green, e);
}
else if (e.FormattedValue == "▼")
{
RenderCellText(Color.Red, e);
}
else
RenderCellText(SystemColors.WindowText, e);
e.Handled = true;
}
}
private void RenderCellText(Color color, DataGridViewCellPaintingEventArgs e)
{
string text = e.FormattedValue.ToString();
string beginning = text.Substring(0, 1);
string end = text.Substring(1);
Point topLeft = new Point(e.CellBounds.X, e.CellBounds.Y + (e.CellBounds.Height / 4));
TextRenderer.DrawText(e.Graphics, beginning, this.dataGridView1.Font, topLeft, color);
Size s = TextRenderer.MeasureText(beginning, this.dataGridView1.Font);
Point p = new Point(topLeft.X + s.Width, topLeft.Y);
TextRenderer.DrawText(e.Graphics, end, this.dataGridView1.Font, p, SystemColors.WindowText);
}
I did something similar once and ended up putting these characters in their own column.
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;
}
ive made a small app using c# for sp 2010, what i am doing is getting items from a collection and viewing specific fields that i want to, the problem is when i click an item it shows me the details of every item assigned to the current user, how can i show only the details of the current item which the user clicks, below is my code...thanks
foreach (SPListItem myItem in myItemCollection)
{
if (myList.Fields.ContainsField("Title"))
{
EntreeListItemDetailNameValue l = lGrp.AddListItem<EntreeListItemDetailNameValue>();
SPField myField1 = myList.Fields.GetField("Title");
l.Name = myField1.Title;
try
{
l.Value = myField1.GetFieldValueAsText(myItem["Title"]);
}
catch
{
l.Value = "";
}
}
if (myList.Fields.ContainsField("Priority"))
{
EntreeListItemDetailNameValue l2 = lGrp.AddListItem<EntreeListItemDetailNameValue>();
SPField myField = myList.Fields.GetField("Priority");
l2.Name = myField.Title;
try
{
l2.Value = myField.GetFieldValueAsText(myItem["Priority"]);
}
catch
{
l2.Value = "";
}
You could use GetItemById()
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.splistitemcollection.getitembyid.aspx
void list_Click(object sender, EventArgs e) {
int clickedid = 0; //get the id from the clicked item
ShowForm(); //show detail form
DataBind(clickedid); //databind detail form
}
void DataBind(int id) {
SPListItemCollection myItemCollection = showthing; //load the list items, query using SPQuery, or SPList.Items
SPListItem item = myItemCollection.GetItemById(id);
form.Title = item["Title"];
}