Colorea diferentes partes de una cadena RichTextBox
Estoy intentando colorear partes de una cadena para agregarla a un RichTextBox. Tengo una cadena construida a partir de diferentes cadenas.
string temp = "[" + DateTime.Now.ToShortTimeString() + "] " +
userid + " " + message + Environment.NewLine;
Así es como se vería el mensaje una vez construido.
[9:23pm] Usuario: mi mensaje aquí.
Quiero que todo lo que esté dentro de los corchetes [9:23] e incluidos sea de un color, 'usuario' sea de otro color y el mensaje sea de otro color. Entonces me gustaría agregar la cadena a mi RichTextBox.
¿Cómo puedo lograr esto?
Aquí hay un método de extensión que sobrecarga el AppendText
método con un parámetro de color:
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;
}
}
Y así es como lo usarías:
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();
Tenga en cuenta que puede notar algo de parpadeo si envía muchos mensajes. Consulte este artículo de C# Corner para obtener ideas sobre cómo reducir el parpadeo de RichTextBox.
He ampliado el método con la fuente como parámetro:
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;
}
Esta es la versión modificada que puse en mi código (estoy usando .Net 4.5) pero creo que debería funcionar también en 4.0.
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();
}
Diferencias con el original:
- Posibilidad de agregar texto a una nueva línea o simplemente agregarlo
- No es necesario cambiar la selección, funciona igual.
- Insertado
ScrollToCaret
para forzar el desplazamiento automático - Agregado
SuspendLayout
/ResumeLayout
requiere un mejor rendimiento
EDITAR: lo siento, esta es una respuesta de WPF
Creo que modificar un "texto seleccionado" en RichTextBox no es la forma correcta de agregar texto en color. Aquí un método para agregar un "bloque de color":
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);
De MSDN :
La propiedad Blocks es la propiedad de contenido de RichTextBox. Es una colección de elementos de párrafo. El contenido de cada elemento de Párrafo puede contener los siguientes elementos:
En línea
Contenedor UI en línea
Correr
Durar
Atrevido
Hipervínculo
Itálico
Subrayar
Salto de línea
Entonces creo que debes dividir la cadena según el color de las partes y crear tantos Run
objetos como sea necesario.
¡Es trabajo para mí! ¡Espero que te sea útil!
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;
}