Ry Goslng
Ry Goslng

Reputation: 13

RichTextBox - How highlight end of line?

If the searched word is at the beginning or end of the line of, I want to color it. If it's in the middle of the line not colorful. I tried many thing but it's not working right.

Seems like beginning of the line is working. But end of the first line only can color. I want to color for all lines end. I think i need index of each line's beggining and loop but i can't do it.

My not working program

How can i fix it?

 private void button1_Click(object sender, EventArgs e)
    {
        int wordLength = textBox1.Text.Length;
        string word = textBox1.Text;     
        for (int i = 0; i < richTextBox1.Lines.Count(); i++)
        {                
            int startIndex = richTextBox1.GetFirstCharIndexFromLine(i);
            richTextBox1.Find(word, startIndex, startIndex+wordLength, RichTextBoxFinds.None);
            richTextBox1.SelectionColor = Color.Red;
            richTextBox1.SelectionBackColor = Color.Yellow;

            int newLineIndex = richTextBox1.Lines[i].Length;
            richTextBox1.Find(textBox1.Text, (newLineIndex - wordLength), newLineIndex, RichTextBoxFinds.None);
            richTextBox1.SelectionColor = Color.Red;
            richTextBox1.SelectionBackColor = Color.Yellow;
        }         

Upvotes: 1

Views: 717

Answers (2)

Jimi
Jimi

Reputation: 32223

I suggest to change your code a little bit. You'll notice why when the RichTextBox text length grows.
Asking for the Lines[] content is not exactly a good thing, much worse in a loop, when you access this Property probably many of times.
You can see in the .Net Source Code what happens (each time - the Lines Property values are not cached and cannot be).

GetLineFromCharIndex() and GetFirstCharIndexFromLine() use instead SendMessage to send the EM_LINEFROMCHAR and EM_LINEINDEX messages to the Edit control - which uses cached values - and are pretty fast.

  • Use Regex.Matches() to collect the indexes of the matched word(s) (you can use more than one word, separated by a pipe: "|", but here we handle just one word. When matching more than one word, use a List<Match> and the Match.Length instead of searchWord.Length) and extract just the Index position of each match.
  • Then, loop the indexes and check whether the current index position meets the criteria.
  • The current line end is found with IndexOf("\n", [StartPosition]), using the first line index (which is also used for the selection) as the starting position.

The RichTextBox Control uses only \n as line separator, so we don't need to worry about \r.

string searchWord = "John";
var txt = richTextBox1.Text;
int textLenght = txt.Length;

// the indexes list can be created with the alternative method (using IndexOf() in a loop) 
var indexes = Regex.Matches(txt, searchWord, RegexOptions.Multiline)
                   .OfType<Match>()
                   .Select(m => m.Index).ToList();

foreach (int index in indexes) {
    int currentLine = richTextBox1.GetLineFromCharIndex(index);
    int lineFirstIndex = richTextBox1.GetFirstCharIndexFromLine(currentLine);
    int lineLastIndex = txt.IndexOf("\n", lineFirstIndex);

    if (index == lineFirstIndex ||
        index == lineLastIndex - searchWord.Length ||
        index == textLenght - searchWord.Length) {
        richTextBox1.Select(index, searchWord.Length);
        richTextBox1.SelectionColor = Color.Red;
    }
}

Edit: Since Regex.Matches is not allowed, you can use IndexOf() in a loop:

var indexes = new List<int>();
int wordPosition = -1;
do {
    if ((wordPosition = txt.IndexOf(searchWord, wordPosition + 1)) >= 0) {
        indexes.Add(wordPosition);
    }
} while (wordPosition >= 0);

Upvotes: 0

naturalbornlazy
naturalbornlazy

Reputation: 301

Try:

 int newLineIndex = i + 1 < richTextBox1.Lines.Length ? richTextBox1.GetFirstCharIndexFromLine(i + 1) - 1 : richTextBox1.TextLength;

Upvotes: 1

Related Questions