Последние два параметра (true, true) указывают на то, что все теги из keywords должны быть цветными и попадать в список слов с функцией авто-завершения. Ясно, что конструкторов в структурах Keyword, Comment (или классах ColorWord, AutoWord и т. д.) должно быть много, столько — сколько надо для удобной работы с ними. Думайте, дерзайте — ваш компонент SyntaxBox должен быть легко управляемым (для клиента).
При разработке компонента (класса SyntaxBox, производного от RichTextBox) вам надо создать множество полей его данных. Предлагаю поместить их объявления в отдельный блок, например:
#region Data Fields
private bool // Поля данных, представленные свойствами
isCaseSensitive, // Комментируйте, не жалея сил
isFiltered; // Комментируйте, не жалея сил
private Highlighter highlighter; // Объект вспомогательного класса
private int // Поля данных для внутреннего использования
pos, // Текущая позиция в тексте документа RTF
start, // Начальная позиция выделенного блока текста
end; // Конечная позиция выделенного блока текста
. . .
private Dictionary<Color, int> colorTable; // Таблица цветов
private StringBuilder rtf; // Черновой вариант текста разметки
. . .
private Stack<UndoInfo> undo, redo;
private bool isUndoing;
. . .
#endregion
В другой блок поместите виртуальные методы, переопределенные в вашем классе, например:
#region Overriden methods
protected override void InitLayout ()
{
isCaseSensitive = isParsing = ignoreLostFocus = isUndoing = false;
isFiltered = true;
. . .
promptList = new ListBox ();
undo = new Stack<UndoInfo> (128); // Это не размер, а Capacity
. . .
base.InitLayout (); // Вызов родительской версии
Controls.Add (promptList);
}
Не жалейте усилий, затрачиваемых на создание IntelliSense-комментариев. Они сильно помогают в процессе разработки — высвечивают введенный вами текст в окнах автозаполнения и окнах типа ToolTip, напоминая вам суть данного метода, и подсказывая верное решение.
/// <summary>
/// Анализ текста, установка цвета, включение окна со списком ключевых слов
/// </summary>
protected override void OnTextChanged (EventArgs e)
{
if (isParsing || TextLength == 0)
return;
isParsing = true;
LockWindowUpdate (Handle); // API: Тормозит перерисовку элемента управления
. . .
base.OnTextChanged (e); // Вызов родительской версии
. . .
Parse ();
. . .
LockWindowUpdate (IntPtr.Zero); // Теперь можно рисовать
Invalidate ();
isParsing = false;
if (promptList.Visible && isFiltered)
CompleteWord ();
}
Мне понадобились некоторые объекты Win32 API. Думаю, что вам также не удастся обойтись без них.
#region Win32 API
public const int
WM_PAINT = 0xF,
WM_KEYDOWN = 0x100,
. . .
EM_GETSCROLLPOS = WM_USER + 221,
EM_SETSCROLLPOS = WM_USER + 222,
. . .
VK_RETURN = 13,
VK_CONTROL = 17,
. . .
VK_DOWN = 40;
[StructLayout (LayoutKind.Sequential)]
public struct POINT { public int x, y; }
. . .
[DllImport ("user32")]
public static extern int SendMessage (IntPtr hwnd, int wMsg, int wParam, IntPtr lParam);
[DllImport ("user32")]
public static extern short GetKeyState (int nVirtKey);
. . .
[DllImport ("user32")]
public unsafe static extern int SetScrollInfo (IntPtr hwnd, int fnBar,
SCROLLINFO* lpsi, bool fRedraw);
#endregion
В любом классе, связанном с обработкой интерфейсных событий, вы можете переопределить виртуальный метод WndProc. Это — аналог оконной процедуры для элемента RichTextBox в мире Win32. Он действует в мире .NET и обслуживает класс (точнее, компонент) SyntaxBox. С помощью этого метода можно более гибко реагировать на события, порождаемые мышью и клавиатурой. Когда я впервые узнал, что в .NET можно переопределить оконную процедуру, на меня это произвело сильное впечатление.
protected override void WndProc (ref Message m)
{
switch (m.Msg)
{
case WM_PAINT: // Не рисуем, пока идет синтаксический разбор
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.