using System; using System.Collections; using System.Drawing; using System.Windows.Forms; using System.Text.RegularExpressions; using System.Runtime.InteropServices; namespace Apex.Forms { public struct HighlightInfo { public int wordStart; public int wordEnd; public int wordLength; } /// /// Schlüsselwort-Hervorhebung für die RichTextBox. /// public class RichTextBoxKeywordHilighter { // Findet den Wortanfang. private static readonly Regex wordStartRegex = new Regex( @"\b\w", RegexOptions.Compiled | RegexOptions.RightToLeft); // Findet das Wortende. private static readonly Regex wordEndRegex = new Regex( @"\w\b", RegexOptions.Compiled); private string[] keywords; private Hashtable keywordLookup; private Color keywordColor = Color.Blue; private RichTextBox richTextBox; private RichTextBoxUpdater updater = new RichTextBoxUpdater(); // Speichert die Selektion während Änderungen vorgenommen werden. private int updating; private int selectionStart; private int selectionLength; private Color selectionColor; /// /// String-Array mit Schlüsselwörtern. /// /// public string[] Keywords { get { return keywords; } set { if(keywords != value) { keywords = value; keywordLookup = null; } } } // Hashtable zum Nachschlagen von Schlüsselwörtern. protected Hashtable KeywordLookup { get { if(keywordLookup == null) { keywordLookup = BuildKeywordLookup(keywords); } return keywordLookup; } } private Hashtable BuildKeywordLookup(string[] keywords) { Hashtable lookup = new Hashtable(); foreach(string k in keywords) { lookup[k] = null; } return lookup; } /// /// Hervorhebungsfarbe der Schlüsselwörter. /// /// public Color KeywordColor { get { return keywordColor; } set { keywordColor = value; } } /// /// Die RichTextBox. /// /// public RichTextBox RichTextBox { get { return richTextBox; } set { richTextBox = value; } } /// /// Hebt ein Schlüsselwort am Index farblich hervor. /// /// /// Das Wort wird vor dem Index gesucht. Der Index darf /// ein Zeichen hinter dem Wort liegen. Das erlaubt es /// als Index die Cursorposition anzugeben, die immer /// um 1 höher als die Position des zuletzt eingegebenen /// Zeichens ist. /// /// Zeichenposition in RichTextBox.Text public HighlightInfo HilightAt(int index) { HighlightInfo hi; // Der gesamte Text. string text = RichTextBox.Text; // Wortanfang ermitteln. int wordStart = index; Match m = wordStartRegex.Match(text, index); if(m.Success) { wordStart = m.Index; } // Wortende ermitteln. int wordEnd = index - 1; m = wordEndRegex.Match(text, wordStart); if(m.Success) { wordEnd = m.Index; } // Wortlänge ermitteln. Ist die Länge 0, gleich zurück. int wordLength = wordEnd - wordStart + 1; hi.wordStart = wordStart; hi.wordEnd = wordEnd; hi.wordLength = wordLength; if(wordLength == 0) return hi; // Das Wort am Index. string word = text.Substring(wordStart, wordLength); // Textfarbe ermitteln. bool isKeyword = KeywordLookup.ContainsKey(word); Color wordColor = isKeyword ? KeywordColor : Color.Black; BeginUpdate(); // Textfarbe ändern. RichTextBox.SelectionStart = wordStart; RichTextBox.SelectionLength = wordLength; RichTextBox.SelectionColor = wordColor; // Spezialfall, wenn ein Wort mit Space in zwei Wörter // aufgetrennt wird, muss zusätzlich das Wort // hinter dem Index verarbeitet werden. bool wordSplit = index < text.Length && index - 2 == wordEnd && Char.IsLetterOrDigit(text[index]); if(wordSplit) { HilightAt(index + 1); } EndUpdate(); return hi; } private int GetWordType(String word, Char c) { if (c=='\n') return 2; else if (c == ' ') return 3; else if (Char.IsLetterOrDigit(c) || (c == '<' && word.IndexOf('>')<0) || c == '>' || (c == '/' && word.IndexOf('<')>=0) ) return 1; else return 0; } private void HighlightWord(String word, int nWordStart, bool bRemark) { Color wordColor = Color.Black; if (bRemark) wordColor = Color.Green; else { bool isKeyword = KeywordLookup.ContainsKey(word); wordColor = isKeyword ? KeywordColor : Color.Black; } // Textfarbe ändern. RichTextBox.SelectionStart = nWordStart; RichTextBox.SelectionLength = word.Length + 1; RichTextBox.SelectionColor = wordColor; } /// /// Hebt alle Schlüsselwörter der RichTextBox farblich hervor. /// public void Hilight() { String text = RichTextBox.Text; if (text.Length == 0) return; int nWordStart = 0; String word = ""; int nWordType = GetWordType(word,text[0]); bool bIsRemark = false; bool bIsLineRemark = false; BeginUpdate(); for (int i = 0; i < text.Length; i++) { if (GetWordType(word, text[i]) != nWordType || i==text.Length-1) { String sTrimmed = word.Trim(); if (sTrimmed == "//") bIsLineRemark = true; if (sTrimmed == "/*") bIsRemark = true; HighlightWord(word, nWordStart, bIsRemark || bIsLineRemark); if (sTrimmed == "*/") bIsRemark = false; if (word == "\n") bIsLineRemark = false; word = Convert.ToString(text[i]); nWordType = GetWordType(word, text[i]); nWordStart = i; } else word += text[i]; } EndUpdate(); } // Sichert die Selektion und deaktiviert // die Aktualisierung der RichTextBox. private void BeginUpdate() { updating++; if(updating > 1) return; // Selektion speichern. selectionStart = RichTextBox.SelectionStart; selectionLength = RichTextBox.SelectionLength; selectionColor = RichTextBox.SelectionColor; // Redraw und Events deaktivieren. this.updater.BeginUpdate(RichTextBox); } // Stellt die ursprüngliche Selektion wieder her // und reaktiviert die Aktualisierung der RichTextBox. private void EndUpdate() { updating--; if(updating > 0) return; // Selektion wiederherstellen. RichTextBox.SelectionLength = selectionLength; RichTextBox.SelectionStart = selectionStart; RichTextBox.SelectionColor = selectionColor; // Redraw und Events aktivieren. this.updater.EndUpdate(RichTextBox); RichTextBox.Invalidate(); } } /// /// Gestattet es Redraw und Events zu deaktivieren, /// während Änderungen im Text einer RichTextBox /// vorgenommen werden. /// /// /// Quelle: Pete's Weblog - Extending RichTextBox, Part I /// http://geekswithblogs.net/pvidler/archive/2003/10/14/182.aspx /// class RichTextBoxUpdater { private const int EM_SETEVENTMASK = 1073; private const int WM_SETREDRAW = 11; private int updating; private int oldEventMask; /// /// Deaktiviert Redraw und Events der RichTextBox. /// /// public void BeginUpdate(RichTextBox rtb) { // Deal with nested calls. updating++; if(updating > 1) return; // Prevent the control from raising any events. oldEventMask = SendMessage( new HandleRef(rtb, rtb.Handle), EM_SETEVENTMASK, 0, 0); // Prevent the control from redrawing itself. SendMessage( new HandleRef(rtb, rtb.Handle), WM_SETREDRAW, 0, 0); } /// /// Reaktiviert Redraw und Events der RichTextBox. /// /// public void EndUpdate(RichTextBox rtb) { // Deal with nested calls. updating--; if(updating > 0) return; // Allow the control to redraw itself. SendMessage( new HandleRef(rtb, rtb.Handle), WM_SETREDRAW, 1, 0); // Allow the control to raise event messages. SendMessage( new HandleRef(rtb, rtb.Handle), EM_SETEVENTMASK, 0, oldEventMask); } [DllImport("user32", CharSet = CharSet.Auto)] private static extern int SendMessage( HandleRef hWnd, int msg, int wParam, int lParam); } }