myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
   » Plugin für Firefox
   » Plugin für IE
   » Gadget für Windows
» Regeln
» Wie poste ich richtig?
» Datenschutzerklärung
» wbb-FAQ

Mitglieder
» Liste / Suche
» Stadt / Anleitung dazu
» Wer ist wo online?

Angebote
» ASP.NET Webspace
» Bücher
» Zeitschriften
   » dot.net magazin
» Accessoires

Ressourcen
» .NET-Glossar
» guide to C#
» openbook: Visual C#
» openbook: OO
» .NET BlogBook
» MSDN Webcasts
» Search.Net

Team
» Kontakt
» Übersicht
» Wir über uns
» Bankverbindung
» Impressum

» Unsere MiniCity
MiniCity
» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Knowledge Base » Artikel » [Tutorial] Keyword-Highlighting (Einfaches Syntax-Highlighting)
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | An Freund senden | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

[Tutorial] Keyword-Highlighting (Einfaches Syntax-Highlighting)

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Pulpapex Pulpapex ist männlich
myCSharp.de-Poweruser/ Experte

Dabei seit: 22.07.2003
Beiträge: 939
Entwicklungsumgebung: Eclipse / VC# 2005
Herkunft: Rostock


Pulpapex ist offline Füge Pulpapex Deiner Kontaktliste hinzu

[Tutorial] Keyword-Highlighting (Einfaches Syntax-Highlighting)

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Einfaches Keyword-Highlighting mit der RichTextBox

Die RichTextBox ist ja im allgemeinen als träge und lahm verschrien. Ich möchte im folgenden zeigen, wie man dennoch schnelles, interaktives Syntax Highlighting hinbekommt. Schlüsselwörter werden bei der Eingabe hervorgehoben.

Die Demo-Anwendung besteht aus vier kleinen Klassen, die ich im folgenden im Detail beschreiben werde:
  • KeywordHilightingTest
  • KeywordHilightingForm
  • RichTextBoxKeywordHilighter
  • RichTextBoxUpdater
KeywordHilightingTest
Zu KeywordHilightingTest gibt es nicht viel zu sagen. Die Klasse enthält nur die Main-Methode:

C#-Code:
class KeywordHilightingTest {

   static void Main() {
      Application.EnableVisualStyles();
      Application.Run(new KeywordHilightingForm());
   }
}

KeywordHilightingForm
KeywordHilightingForm ist auch sehr einfach. Als einziges Control befindet sich eine RichTextBox mit DockStyle.Fill auf der Form. Für das RichTextBox.TextChanged-Ereignis wurde ein EventHandler eingerichtet. Das Ereignis wird bei jedem Tastendruck ausgelöst und ruft die HilightAtCursor-Methode auf. Desweiteren enthält die Klasse noch ein String-Array mit den Schlüsselwörtern und verfügt über eine KeywordHilighter-Eigenschaft, die das eigentliche Hervorheben übernimmt.

C#-Code:
public class KeywordHilightingForm : Form {

   private RichTextBoxKeywordHilighter keywordHilighter;

   private readonly string[] keywords = {
      "abstract", "event", "new", "struct",
      "as", "explicit", "null", "switch",
      "base", "extern", "object", "this",
      "bool", "false", "operator", "throw",
      "break", "finally", "out", "true",
      "byte", "fixed", "override", "try",
      "case", "float", "params", "typeof",
      "catch", "for", "private", "uint",
      "char", "foreach", "protected", "ulong",
      "checked", "goto", "public", "unchecked",
      "class", "if", "readonly", "unsafe",
      "const", "implicit", "ref", "ushort",
      "continue", "in", "return", "using",
      "decimal", "int", "sbyte", "virtual",
      "default", "interface", "sealed", "volatile",
      "delegate", "internal", "short", "void",
      "do", "is", "sizeof", "while",
      "double", "lock", "stackalloc",
      "else", "long", "static",
      "enum", "namespace", "string"
   };

   public KeywordHilightingForm() {
      InitializeComponent();
   }

   private void InitializeComponent() {
      // ...
   }

   // Wird bei jedem Tastendruck ausgelöst.
   private void HandleTextChanged(object sender, EventArgs e) {
      HilightAtCursor();
   }

   // Hebt den Text am Cursor farblich hervor.
   public void HilightAtCursor() {

      // Entspricht Cursorposition.
      int index = this.richTextBox.SelectionStart;

      // Wort am Cursor hervorheben.
      KeywordHilighter.HilightAt(index);
   }

   // Der KeywordHilighter übernimmt das Highlighten.
   // Er wird mit der RichTextBox und den Keywords initialisiert.
   public RichTextBoxKeywordHilighter KeywordHilighter {
      get {
         if(keywordHilighter == null) {
            keywordHilighter = new RichTextBoxKeywordHilighter();
            keywordHilighter.RichTextBox = this.richTextBox;
            keywordHilighter.Keywords = this.keywords;
         }
         return keywordHilighter;
      }
   }
}

RichTextBoxKeywordHilighter
Die RichTextBoxKeywordHilighter-Klasse ist wie gesagt für das eigentliche Hervorheben zuständig. Der Trick besteht darin, so wenig Text wie nur möglich zu bearbeiten. Deshalb wird auch nur das Wort vor dem Cursor untersucht, das gerade eingegeben wird. Die gesamte Textzeile zu aktualisieren wäre zwar einfacher, dauert aber schon zu lange.

Der Code für die Texthervorhebungen befindet sich in der HilightAt-Methode. Als Parameter wird ein Index auf ein Zeichen übergeben. Der Index darf um 1 grösser sein als die Position des letzten Zeichens im hervorzuhebenden Wort. Er entspricht damit der Cursorposition, wenn das Wort gerade eingegeben wird. Den Code der Methode habe ich hier in mehrere Blöcke aufgeteilt, um ihre Funktion besser beschreiben zu können.

Zunächst einmal wird der Text aus der RichTextBox benötigt. RichTextBox ist eine Eigenschaft von RichTextBoxKeywordHilighter.

C#-Code:
// Der Text.
string text = RichTextBox.Text;

Jetzt kann das Wort vor der Cursorposition gesucht werden. Zuerst wird der Wortanfang ermittelt.

C#-Code:
// Wortanfang ermitteln.
int wordStart = index;
Match m = wordStartRegex.Match(text, index);
if(m.Success) {
   wordStart = m.Index;
}

Hier der im Code verwendete, reguläre Ausdruck wordStartRegex. Er sucht ausgehend vom Cursor rückwärts:

C#-Code:
private static readonly Regex wordStartRegex = new Regex(
   @"\\b\\w",
   RegexOptions.Compiled | RegexOptions.RightToLeft);

Ab Wortanfang wird wieder vorwärts nach dem Wortende gesucht.

C#-Code:
// Wortende ermitteln.
int wordEnd = index - 1;
m = wordEndRegex.Match(text, wordStart);
if(m.Success) {
   wordEnd = m.Index;
}

Hier der zugehörige, reguläre Ausdruck wordEndRegex:

C#-Code:
private static readonly Regex wordEndRegex = new Regex(
   @"\\w\\b",
   RegexOptions.Compiled);

Aus Wortanfang und Wortende wird die Wortlänge ermittelt. Ist sie 0, gibt es nichts zu highlighten und es kann sofort abgebrochen werden:

C#-Code:
// Wortlänge ermitteln. Ist die Länge 0, gleich zurück.
int wordLength = wordEnd - wordStart + 1;
if(wordLength == 0) return;

Wurde ein Wort gefunden, wird überprüft ob es sich um ein Schlüsselwort handelt. Dementsprechend wird eine Textfarbe ausgewählt. KeywordColor ist wieder eine Eigenschaft von RichTextBoxKeywordHilighter.

C#-Code:
// Das Wort am Index.
string word = text.Substring(wordStart, wordLength);

// Textfarbe ermitteln.
bool isKeyword = KeywordLookup.ContainsKey(word);
Color wordColor = isKeyword ? KeywordColor : Color.Black;

Nun folgt das eigentliche Hervorheben. Zuvor muss aber noch die aktuelle Selektion der RichTextBox gesichert werden. Sie legt auch die Cursorposition fest, die im Anschluss wieder hergestellt werden muss. Das geschied in den Methoden BeginUpdate und EndUpdate.

C#-Code:
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 aktualisiert werden.
bool wordSplit = index < text.Length &&
   index - 2 == wordEnd &&
   Char.IsLetterOrDigit(text[index]);
if(wordSplit) {
   HilightAt(index + 1);
}

EndUpdate();

Wie man am Code sieht, gibt es einen Sonderfall, der extra behandelt werden muss. Normalerweise wird nur das Wort vor dem Cursor beachtet. Wird jedoch ein Wort durch einen Tastendruck in zwei Wörter aufgetrennt, kann sich auch die Farbe des Wortes hinter dem Cursor ändern. In dem Fall wird HilightAt einfach ein zweites Mal aufgerufen.


Der Code, wie er jetzt ist, funktioniert eigentlich schon zufriedenstellend. Es gibt keine Verzögerungen beim Tippen und gleichzeitigem Highlighting. Allerdings sind die in HilightAt programmatisch vorgenommenen Selektionen für den Benutzer sichtbar. Sie fallen durch kurzes Aufblitzen unangenehm auf. Dieses Problem löst die RichTextBoxUpdater-Klasse. Den Code hierfür habe ich in Pete's Weblog gefunden:

Pete's Weblog:  Extending RichTextBox, Part I


RichTextBoxUpdater
RichTextBoxUpdater definiert zwei Methoden, BeginUpdate und EndUpdate. BeginUpdate deaktiviert die Aktualisierung der RichTextBox-Anzeige während vom Programm Textänderungen vorgenommen werden. EndUpdate reaktiviert die Aktualisierung wieder. Leider gibt es in der RichTextBox-Klasse keine Methoden, die das erledigen. Es muss also wiedermal auf P/Invoke und die Win-Api zurückgegriffen werden.

Die Einzelheiten können auf der angegebenen Seite nachgelesen werden, hier nur noch der Code für die Klasse:

C#-Code:
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);
}

Das war's auch schon. So sieht das Ergebnis aus:




Wenn ihr Lust habt, probiert die angehängte Demo-Anwendung aus. Die Zip-Datei enthält auch den kompletten Quellcode, den ihr nach Belieben zerpflücken und in eigene Anwendungen einbauen könnt. Bei Pete's Code bin ich mir nicht so sicher, aber ich denke, dass eine Quellenangabe ausreichen sollte.

In einem zweiten Teil werde ich die RichTextBoxKeywordHilighter-Klasse nochmal um das Highlighten für Textbereiche erweitern, damit sie auch beim Laden von Textdateien und bei Copy&Paste funktioniert.


Gruss
Pulpapex

Suchhilfe: Syntax, Keyword, Keywords, Highlight, Hilight, Highlighter, Hilighter, Highlighting, Hilighting, Syntaxhighlight, Syntaxhilight, Syntaxhighlighter, Syntaxhilighter, Syntaxhighlighting, Syntaxhilighting, Keywordhighlight, Keywordhilight, Keywordhighlighter, Keywordhilighter, Keywordhighlighting, Keywordhilighting


Dateianhang:
zip KeywordHilighting.zip (9,41 KB, 2.415 mal heruntergeladen)
12.04.2005 21:45 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 4 Monate.
Fabian Fabian ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-1590.jpg


Dabei seit: 09.12.2004
Beiträge: 1.979
Entwicklungsumgebung: Visual Studio 2010
Herkunft: Dortmund


Fabian ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Pulpapex,

erstmal ein großes Danke für den Artikel. Ist sehr gut beschreiben und einfach erweiterbar.

Willst Du evtl. noch weiter dran arbeiten? Ich habe so ein paar Erweiterungen, die ich nicht ganz hinbekomme (liegt hauptsächlich daran, dass ich keine Ahnung von RegEx habe).

Ein Bereich, der auf Kommentare matched, wäre sehr gut. Ich komme leider nicht auf den entsprechenden RegEx-Ausdruck.

Auch das Laden von Dateien bzw. Copy & Paste wären nicht schlecht. Da komme ich auch nicht dahinter unglücklich .

Würde mich freuen, wenn Du noch mal Lust hast, daran weiter zu arbeiten.


Gruß,
Fabian
31.08.2005 10:13 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
nitronic nitronic ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-1597.jpg


Dabei seit: 14.03.2004
Beiträge: 354
Entwicklungsumgebung: VS 2008 / 2005 / Eclipse
Herkunft: Österreich


nitronic ist offline Füge nitronic Deiner Kontaktliste hinzu MSN-Passport-Profil von nitronic anzeigen

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Copy & Paste fehlt noch ein wenig in deiner Lösung, und bei ca. 4000 Zeilen in der RichTextBox flackert das Teil schon gewaltig.
01.09.2005 13:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Pulpapex Pulpapex ist männlich
myCSharp.de-Poweruser/ Experte

Dabei seit: 22.07.2003
Beiträge: 939
Entwicklungsumgebung: Eclipse / VC# 2005
Herkunft: Rostock

Themenstarter Thema begonnen von Pulpapex

Pulpapex ist offline Füge Pulpapex Deiner Kontaktliste hinzu

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hi nitronic,

Highlighting für Textbereiche, also für Copy & Paste, kommt im zweiten Teil. Das steht ganz unten im Artikel.

Im Moment wird nur in der Umgebung des Cursors hervorgehoben. Deshalb wundert es mich, dass es bei viel Text flimmern soll. Sollte eigentlich keine Rolle spielen. Ich werde das heute abend mal testen.

@Fabian - den zweiten Teil werde ich die nächsten Tage in Angriff nehmen. Mal schauen.
01.09.2005 14:14 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Fabian Fabian ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-1590.jpg


Dabei seit: 09.12.2004
Beiträge: 1.979
Entwicklungsumgebung: Visual Studio 2010
Herkunft: Dortmund


Fabian ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Pulpapax,

das wäre auf jeden Fall sehr nett von Dir. Das Beispiel ist richtig gut.

Ein Problem habe ich noch beim Implementieren der Erkennung von Kommentaren. Kannst Du da vielleicht auch mal drüber gucken?


Gruß,
Fabian
01.09.2005 16:07 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 2 Jahre.
TheProgrammer TheProgrammer ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.10.2007
Beiträge: 3
Entwicklungsumgebung: Visual Studio C# 2005 Express


TheProgrammer ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Vielen dank für das Tutorial. Habs gerade gefunden und es hat mir sehr geholfen.
Allerdings hab ich einen Kritikpunkt. Deine Klasse kann keine Schlüsselwörter wie "<html>" hervorheben sondern nur "html"

mfg
TheProgrammer
28.10.2007 17:53 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Team (Admin)

images/avatars/avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.092
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo TheProgrammer,

dazu wäre nur eine kleinere Änderung an den verwendeten Regex-Pattern nötig. Die momentanen Pattern arbeiten ja absichtlich mit Wortgrenzen (\b), damit bei "forget" nicht fälschlich "for" innerhalb des Wortes hervorgehoben würde.

Da Pulpapex schon lange nicht mehr im Forum war, wirst du die Änderung selber durchführen müssen. Mach für evtl. Fragen zu Regex oder der überhaupt der Umsetzung bitte einen neuen Thread auf.

herbivore
28.10.2007 18:03 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 6 Monate.
Jdv Jdv ist männlich
myCSharp.de-Mitglied

Dabei seit: 19.03.2008
Beiträge: 1


Jdv ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hi,

für alle die es interessiert.. ich habe den RichTextBoxKeywordHighlighter etwas erweitert.. er kann jetzt den gesamten Text highlighten mit <html> und remarks..

Gruß

Joachim


Dateianhang:
unknown RichTextBoxKeywordHilighter.cs (9,19 KB, 678 mal heruntergeladen)
15.05.2008 11:11 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 5 Monate.
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

images/avatars/avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 4.501


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hi!

Ich stelle grade fest, der RichtextboxUpdater funzt nur halb. In RichtextboxUpdater.BeginUpdate()

C#-Code:
      // Prevent the control from raising any events.
      oldEventMask = SendMessage(
         new HandleRef(rtb, rtb.Handle),
         EM_SETEVENTMASK, 0, 0);
      //...

ließ mich ja erwarten, daß insbesondere die SelectionChanged-Events der Rtb unterdrückt würden. Werdenseabernich.

mein kleiner Test:

C#-Code:
        public RichTextBox RichTextBox {
         get { return richTextBox; }
         set {
            richTextBox = value;
            richTextBox.SelectionChanged += new EventHandler(richTextBox_SelectionChanged);
         }
      }

      void richTextBox_SelectionChanged(object sender, EventArgs e) {
         if (updating > 0) {
            throw new Exception(
               "Dieses Ereignis sollte doch in diesem Zustand nicht gefeuert werden!");
         }
      }

Ich erweitere den Richtextbox-Setter, und abonniere das SelectionChanged.

Anbei das Werk in lauffähiger Vollständigkeit:


Dateianhang:
zip KeywordHighlighter00.zip (14 KB, 1.076 mal heruntergeladen)
24.10.2008 22:16 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 9 Jahre.
Der letzte Beitrag ist älter als 6 Jahre.
Antwort erstellen


© Copyright 2003-2014 myCSharp.de-Team. Alle Rechte vorbehalten. 24.11.2014 23:17