Подсветка синтаксиса RichTextBox в реальном времени - отключение перерисовки - PullRequest
12 голосов
/ 19 июля 2010

Я создаю функцию, которая принимает RichTextBox и имеет доступ к списку ключевых слов и «плохих слов». Мне нужно выделить любые ключевые слова и плохие слова, которые я нахожу в RichTextBox , когда пользователь печатает , что означает, что функция вызывается каждый раз, когда выпускается клавиша редактирования.

Я написал эту функцию, но слова и курсор в окне слишком сильно мерцают для удобства.

Я нашел решение - отключить возможность RichTextBox перекрашивать себя, пока я редактирую и форматирую его текст. Однако единственный способ, которым я знаю, это переопределить функцию «WndProc» и перехватить (что я собирался собрать) сообщение перерисовки следующим образом:

protected override void WndProc(ref System.Windows.Forms.Message m)
{
    if (m.Msg == 0x00f) {
         if (paint)
            base.WndProc(ref m);
         else
            m.Result = IntPtr.Zero;
    }
    else
         base.WndProc(ref m);
}

Где булево значение «paint» установлено в false перед тем, как я начинаю выделять, и в true, когда я заканчиваю. Но, как я уже сказал, функция, которую я выполняю, должна принимать RichTextBox; Я не могу использовать подкласс.

Итак, есть ли способ отключить автоматическое перекрашивание RichTextBox 'извне?'

Ответы [ 3 ]

24 голосов
/ 19 июля 2010

Это недосмотр в классе RichTextBox. Другие элементы управления, такие как ListBox, поддерживают методы BeginUpdate и EndUpdate для подавления рисования. Эти методы генерируют сообщение WM_SETREDRAW. RTB фактически поддерживает это сообщение, но они забыли добавить методы.

Просто добавь их сам. Project + Add Class, вставьте код, показанный ниже. Скомпилируйте и перетащите элемент управления из верхней части панели инструментов на форму.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class MyRichTextBox : RichTextBox {
    public void BeginUpdate() {
        SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
    }
    public void EndUpdate() {
        SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero); 
        this.Invalidate();
    }
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    private const int WM_SETREDRAW = 0x0b;
}

Или P / вызвать SendMessage непосредственно перед / после обновления текста.

5 голосов
/ 10 января 2011

Я не набрал достаточно очков, чтобы изменить рекомендацию Ганса. Поэтому я добавил этот ответ , чтобы упомянуть, что, возможно, потребуется запросить перерисовку, вызвав InvalidateRect . Некоторые реализации Begin / End Update делают это автоматически после окончательного выпуска блокировки обновления. Аналогично в .Net можно вызывать Control . Invalidate (), который вызывает собственную функцию InvalidateRect .

MSDN: Наконец, приложение может вызвать функцию InvalidateRect, чтобы вызвать перерисовку списка.

См. WM_SETREDRAW

0 голосов
/ 19 июля 2010

Лучше всего выполнить то, что вы пытаетесь сделать, - создать многопоточное приложение. Вы захотите создать один поток, который проверяет текст по вашему списку. Этот поток помещает любые найденные экземпляры в очередь. Вы также захотите создать другой поток, который будет делать фактическое выделение слов. Поскольку вам нужно использовать BeginInvoke () и Invoke () для обновления пользовательского интерфейса, вам нужно убедиться, что вы ограничиваете скорость, с которой это вызывается. Я бы так не более 20 раз в секунду. Для этого вы должны использовать такой код:

DateTime lastInvoke=DateTime.Now;

if ((DateTime.Now - lastInvoke).TotalMilliseconds >=42)
{
    lastInvoke=DateTime.Now;
    ...Do your highlighting here...
}

Этот поток проверит вашу очередь на наличие слов, которые необходимо выделить или повторно выделить, и будет постоянно проверять очередь на наличие новых обновлений. Надеюсь, что это имеет смысл!

...