Lekce 17 - Tvoříme vlastní Syntax Highlighter pomocí RichTextBox v C#
V předchozích 4 lekcích jsme pracovali se základními ovládacími prvky, které nabízí sám .NET Framework. Toto počínání jsme dovršili lekcí Ovládací prvky Windows Forms počtvrté.
V dnešním C# .NET Windows Forms tutoriálu naprogramujeme vlastní Syntax
Highlighter, tedy zvýrazňovač syntaxe v C# .NET, jako má např. Visual
Studio. Procvičíme si tím použití ovládacího prvku
RichTextBox:

Návrh
Nejprve je třeba si rozvrhnout, jak vlastně bude náš program fungovat. Nejjednodušší, co určitě většinu z vás napadne, je mít nějakou kolekci s klíčovými slovy a jejich barvou, tu projet cyklem a podle toho text obarvovat. A přesně tak to i uděláme. Samozřejmě nesmíme očekávat extra výkon a chtít zvýrazňovač použít pro dlouhý kód. Hodí se spíše pro jednorázové zvýraznění. Ale my si vyzkoušíme i realtime. Efektivní zvýraznění bychom museli udělat pomocí tokenizace, což je nad rámec tohoto kurzu.
Návrh formuláře
Vytvoříme si novou Windows Forms aplikaci s názvem
EasySyntaxHighlighter. Začneme tradičně návrhem formuláře.
Přidáme do něj ovládací prvky:
RichTextBoxna kód ke zvýraznění aButtonna zapnutí zvýraznění, realtime zvýraznění si totiž pro zjednodušení přidáme až později.
RichTextBox bude moci psát odsazení tabulátorem, proto mu
povolíme AcceptsTab. Tlačítku vytvoříme událost
OnClick.
Slovníky
Nyní si vytvoříme třídu SyntaxHighlighter.
Vytvoření slovníku
Přidáme do ní statickou kolekci keywords typu
Dictionary s typem klíče string a hodnotou
Color:
private static Dictionary<string, Color> keywords;
Dále přidáme veřejnou statickou metodu
void InitializeKeywords(), ve které kolekci inicializujeme a
vložíme do ní naše klíčová slova. Můžeme přidat také jednoduché
ošetření if (keywords != null) return;, aby metoda zbytečně
neproběhla vícekrát.
Statická je kolekce a metoda proto, že tato klíčová slova budou vždy stejná a je zbytečné ukládat je pro každou instanci highlighteru.
Naplnění slovníku
Najdeme si klíčová slova, která budeme zvýrazňovat. Budeme zvýrazňovat samozřejmě klíčová slova C#.
Klíčová slova z daných odkazů jednoduše zkopírujeme a v textovém editoru ořežeme tak, aby z nich vznikl dlouhý řádek jednotlivých slov oddělených čárkou, jako:
abstract,event,new,struct,as,explicit,null,switch,base,extern,this,false,operator,throw,break...
Teď je třeba smazat duplicitní slova. To můžeme udělat buď ručně
nebo si na to napsat malý C# program, který slova rozdělí podle čárky,
cyklem přidá do kolekce List a pak zavolá
Distinct().
Klíčová slova je pak dobré roztřídit. Budeme rozlišovat jinou barvou datové typy a jinou ostatní klíčová slova. Klíčová slova budou třeba zelená, kdežto datové typy modré.
Ve výsledku dospějeme k následujícím řetězcům, které slova
definují. Pokud se vám nechce sestavovat si tyto definice ručně, můžete si
jen zkopírovat tyto proměnné. Vložíme je dále do metody
InitializeKeywords():
string CSharpKeywords = "abstract,event,new,struct,as,explicit,null,switch,base,extern,this,false,operator,throw,break,finally,out,true,fixed,override,try,case,params,typeof,catch,for,private,foreach,protected,checked,goto,public,unchecked,if,readonly,unsafe,implicit,ref,continue,in,return,using,virtual,default,interface,sealed,volatile,delegate,internal,do,is,sizeof,while,double,lock,stackalloc,else,static,namespace"; string DataTypes = "bool,object,byte,float,class,uint,char,ulong,ushort,const,decimal,int,sbyte,short,void,long,enum,string";
No a teď už jen jednoduše projedeme cyklem jednotlivá slova a přidáme je do kolekce s příslušnou barvou:
foreach (string word in CSharpKeywords.Split(',')) keywords.Add(word, Color.Green); foreach (string type in DataTypes.Split(',')) keywords.Add(type, Color.Blue);
Tuto metodu zavoláme v konstruktoru formuláře, který v něm již máme:
public Form1()
{
InitializeComponent();
SyntaxHighlighter.InitializeKeywords();
}
Zvýraznění
Nyní se přesuneme zpět do třídy SyntaxHighlighter.
Přidáme sem veřejnou statickou metodu
HighlightText(), která vrací string a jejím
parametrem je string text.
Metoda je opět statická, protože
SyntaxHighlighter nenese žádná instanční data, je to jen
takový pomocník. Jestli chcete, můžete si celou třídu označit jako
static.
V metodě si vytvoříme další pomocnou instanci RichTextBox,
ve které si zvýrazněný dokument připravíme:
RichTextBox rtb = new RichTextBox(); rtb.Text = text; rtb.Font = new Font(FontFamily.GenericMonospace, 8.75f, FontStyle.Regular);
Obarvení klíčových slov
Dále projedeme všechna klíčová slova, vyhledáme je v textu a obarvíme příslušnou barvou:
int index; foreach (var entry in keywords) { index = 0; while ((index = rtb.Find(entry.Key, index, RichTextBoxFinds.WholeWord)) != -1) { rtb.Select(index, entry.Key.Length); rtb.SelectionFont = new Font(rtb.Font.FontFamily, rtb.Font.Size, FontStyle.Bold); rtb.SelectionColor = entry.Value; index += 1; } }
Proměnná index určuje současnou pozici v textu. Při
hledání dalšího slova se postupuje od pozice toho předchozího. Přičítá
se k němu 1, protože je v něm obsažena pozice, kde začíná
dané slovo a program by se zacyklil. Takto slovo usekneme a bude se hledat
dál.
Metoda Find() vyhledá dané slovo, začíná hledat od dané
pozice a hledá pouze samostatná slova, tzn. např. "in" najde,
ale "indeed" už ne. Vrátí pozici, kde dané slovo začíná.
Pokud se v textu nevyskytuje, vrátí -1.
Metoda Select() vybere text, který začíná na dané pozici a
označí zadaný počet znaků. Dělá to samé, jako když klepneme a táhneme
myší přes text.
Nyní, když je text vybrán, zvolí se barva a písmo výběru pomocí
SelectionFont a SelectionColor.
Můžeme si dále zvýraznit i komentáře:
index = 0; while ((index = rtb.Text.IndexOf("//", index)) != -1) { rtb.Select(index, rtb.Text.IndexOf("\n", index) - index); rtb.SelectionFont = new Font(rtb.Font.FontFamily, rtb.Font.Size, FontStyle.Regular); rtb.SelectionColor = Color.Gray; index += 2; // zde se přičítá dvojka kvůli popiskům tzv. summary, kde jsou /// závorky, já je beru jako komentář. }
Nebo i string "můj text" či více řádkové
komentáře /* můj komentář */, které lze jednoduše přidat
stejným způsobem jako jednořádkový komentář (viz projekt ke stažení pod
lekcí).
Testování
Nyní si zvýrazňovač vyzkoušíme. Pouze do události tlačítka přidáme:
richTextBox1.Rtf = SyntaxHighlighter.HighlightText(richTextBox1.Text);
A spustíme 
Real-time zvýrazňování
Nyní si zkusíme real time zvýrazňování. Tzn., že budeme psát a bude
se nám text ihned zvýrazňovat. Tlačítko smažeme i s událostí.
RichTextBox nastavíme dock, aby se nám pěkně roztáhl. A
přidáme mu událost KeyDown, kde zvýraznění zavoláme.
Pokud jste někteří zkoušeli s předstihem, narazili jste pravděpodobně
na problém, že se vlastně nedá psát. Při zvýraznění se nám totiž
kurzor začne vracet na začátek. Tento problém ošetříme jednoduše
uložením současné pozice kurzoru SelectionStart, zvýrazněním
textu a následným nastavením pozice kurzoru zpět. Kód obslužné metody pro
událost KeyDown je tedy následující:
int lastSelectionStart = richTextBox1.SelectionStart;
richTextBox1.Rtf = SyntaxHighlighter.HighlightText(richTextBox1.Text);
richTextBox1.SelectionStart = lastSelectionStart;
Bystří z vás narazí na další problém, že pokud chceme něco označit a smazat, nejde to. To se dá snadno ošetřit kontrolou, zda je něco vybraného, tzn. délka výběru. Na začátek té samé obslužné metody přidáme:
if (richTextBox1.SelectionLength > 0) return;
A náš vlastní highlighter je na světě! V přiloženém archivu si můžete stáhnout verzi, která zvýrazňuje i textové řetězce a také jazyk PHP:

V příští lekci, Vlastní ovládací prvky v C# .NET, si ukážeme jak si vytvořit svůj vlastní ovládací prvek.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 436x (49.16 kB)
Aplikace je včetně zdrojových kódů v jazyce C#
