Sign in to follow this  
Zyndrof

[.net] RichTextBox: coloring selection?

Recommended Posts

Hi! I'm using a RichTextBox for a project and I want it to search the newly written text for certain words and color them. The problem I have is that no matter what I try the coloring isn't made on the selection but on all the text except for the very first letter. Why is that and how do I solve it?
Private Sub rtbEditor_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rtbEditor.TextChanged

    rtbEditor.Find("hello", RichTextBoxFinds.MatchCase)
    rtbEditor.SelectionColor = Color.Purple

End Sub

Share this post


Link to post
Share on other sites
I tried to replicate your problem, but could not. (It seems to work)
Here is the code that I was using.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace TestRtfColor
{
public partial class Form1 : Form
{
RichTextBox t;
Button b, c, d;

public Form1()
{
InitializeComponent();

this.Height = 300;

t = new RichTextBox();
t.Top = 0;
t.Left = 0;
t.Text = "abcdefghijklmnopqrstuvwxyz" + Environment.NewLine;
t.Text += "bla hello bla" + Environment.NewLine;
t.Text += "more text";
this.Controls.Add(t);

b = new Button();
b.Text = "blue";
b.Top = t.Bottom;
b.Left = 0;
b.Click += new EventHandler(b_Click);
this.Controls.Add(b);

c = new Button();
c.Text = "red";
c.Top = b.Bottom;
c.Left = 0;
c.Click += new EventHandler(c_Click);
this.Controls.Add(c);

d = new Button();
d.Text = "find";
d.Top = c.Bottom;
d.Left = 0;
d.Click += new EventHandler(d_Click);
this.Controls.Add(d);
}

void d_Click(object sender, EventArgs e)
{
t.Find("hello", RichTextBoxFinds.MatchCase);
t.Focus();
}

void c_Click(object sender, EventArgs e)
{
t.SelectionColor = Color.Red;
}

void b_Click(object sender, EventArgs e)
{
t.SelectionColor = Color.Blue;
}
}
}

Share this post


Link to post
Share on other sites
Well, the difference is that my rtb is empty and I let the user fill it. If I do it your way it works, but that's not the way I want it. I want it to change by itself while writing.

Share this post


Link to post
Share on other sites
If you are trying to perform some sort of syntax highlighting (and it sounds like you are), then I would suggest that instead of trying to create your own solution, consider re-using a control designed to do that. The ICSharpCode.TextEditor is one such control. It is the code that is used in the SharpDevelop IDE.

It is LGPL code (Version 2.0 and later of SharpDevelop) and since the assembly that contains ICSharpCode.TextEditor would be a seperate library and not static linking you should be able to use it other projects. If you are working on a comercial project, I recommend having a lawyer confirm this and not just take my word for it.

I use it in my code and it works quite well. Here is a screen shot of it in use in my app. http://www.k2wrpg.org/newsimages/20060413A.png. I just use it for syntax highlighting, but its possible to use it for code completion as well. But that may be more than what you are looking for.

Share this post


Link to post
Share on other sites
If you want to set colour, you need to set the selection area first. Find() simply tells you where the substring starts (well, from what I can tell it does this), you would then need to set the selection area,

SelectionStart = Find("hello", RichTextBoxFinds.MatchCase);
SelectionLength = "hello".Length;
SelectionColour = ...;

However, this is pretty ugly, as you will see flickering.

So, the better solution is to modify the RTF string. You can do this either by modifying the existing RTF, or by generating your own. If you do not plan to allow font changes, etc, then the later option is better.

This is a class I made a while ago that will generate a RTF string, I used this for to code highlighting:


/// <summary>
/// A simple class that will construct a rich text style from text and colour input
/// </summary>
public class RichTextConstructor
{
SortedList<int, string> colourTable = new SortedList<int, string>();
List<Segment> segments = new List<Segment>(32);
string header, header2;
string colors;
int colourCount = -1;

/// <summary>
/// Constructor for the rich text
/// </summary>
/// <param name="fontname">exact name of the desired font (eg, "Courier New" - without quotes)</param>
/// <param name="pixelHeight">pixel height of the desired font, 20 is a recommended starting value</param>
public RichTextConstructor(string fontname, int pixelHeight)
{
header = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang5129{\\fonttbl{\\f0\\fnil\\fcharset0 " + fontname + ";}}{\\colortbl ;";
header2 = "}\\viewkind4\\uc1\\pard\\f0\\fs" + pixelHeight + " ";
}

struct Segment
{
public string segment;
public Color f, b;
}

/// <summary>
/// Clear the generated text
/// </summary>
public void Clear()
{
segments.Clear();
}

/// <summary>
/// Add Text segement
/// </summary>
/// <param name="text">text to add</param>
/// <param name="foreground">foreground (text) colour</param>
/// <param name="background">background (highlight) colour</param>
public void AddSegment(string text, Color foreground, Color background)
{
Segment s;
s.segment = text;
s.b = background;
s.f = foreground;

if (colourTable.ContainsKey(foreground.ToArgb()) == false)
colourTable.Add(foreground.ToArgb(), null);
if (colourTable.ContainsKey(background.ToArgb()) == false)
colourTable.Add(background.ToArgb(), null);

StringBuilder rebuild = null;
int from = 0;
int i = 0;

for (i = 0; i < text.Length; i++)
{
char c = text[i];


if (c == '\\' || c == '}' || c == '{' || c == '\n' || c > 255) // invalid character
{
if (rebuild == null)
rebuild = new StringBuilder(text.Length + 8);

rebuild.Append(text, from, i - from);
from = i + 1;

switch (c)
{
case '\\':
case '}':
case '{':
rebuild.Append('\\');
rebuild.Append(c);
break;

case '\n':
rebuild.Append("\\par\n");
break;
case '\t':
rebuild.Append("\\tab ");
break;
}
if (c > 255) //unicode
{
rebuild.Append("\\u");
rebuild.Append(((int)c).ToString());
rebuild.Append('?');
}
}
}

if (rebuild != null)
{
rebuild.Append(text, from, i - from);
s.segment = rebuild.ToString();
}

segments.Add(s);
}

/// <summary>
/// Remove the last added segment
/// </summary>
public void TrimSegment()
{
if (segments.Count>0)
segments.RemoveAt(segments.Count - 1);
}

/// <summary>
/// Generates the rich text string
/// </summary>
/// <returns></returns>
public string ConstructRichTextString()
{
StringBuilder sb = new StringBuilder(1024);
sb.Append(header);


if (colourCount != colourTable.Count)
{
SortedList<int, string> newColourTable = new SortedList<int, string>();
StringBuilder colorsSb = new StringBuilder(32 * colourTable.Count);
for (int i = 0; i < colourTable.Count; i++)
{
newColourTable.Add(colourTable.Keys[i], (i + 1).ToString() + " ");

Color c = Color.FromArgb(colourTable.Keys[i]);

colorsSb.Append("\\red");
colorsSb.Append(c.R.ToString());
colorsSb.Append("\\green");
colorsSb.Append(c.G.ToString());
colorsSb.Append("\\blue");
colorsSb.Append(c.B.ToString());
colorsSb.Append(";");

}

colors = colorsSb.ToString();
colourTable = newColourTable;
colourCount = colourTable.Count;
}

sb.Append(colors);
sb.Append(header2);

//fill text

Color? fore = null;
Color? back = null;

foreach (Segment seg in segments)
{
if (fore != seg.f)
{
sb.Append("\\cf");
sb.Append(colourTable[seg.f.ToArgb()]);
fore = seg.f;
}
if (back != seg.b)
{
sb.Append("\\highlight");
sb.Append(colourTable[seg.b.ToArgb()]);
back = seg.b;
}

sb.Append(seg.segment);
}

sb.Append("\\par\n}");
return sb.ToString();
}
}





It's not perfect but it gets the job done. And is nice and fast

Share this post


Link to post
Share on other sites
Yes, what I wanna do is syntax lightning. I'm making my own HTML-editor as I think that it is a project that I can learn a lot from. It can start small and grow very big. What tecnique should I use for this? I have realized that the Find() method may not be the best.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this