【问题标题】:RichTextBox syntax highlighting in real time--Disabling the repaintRichTextBox 语法实时高亮--禁用重绘
【发布时间】:2023-03-26 04:56:01
【问题描述】:

我正在创建一个函数,该函数采用 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 的自动重绘?

【问题讨论】:

    标签: c# graphics richtextbox syntax-highlighting


    【解决方案1】:

    这是 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/Invoke SendMessage。

    【讨论】:

    • 这个类的添加对我不起作用。它会导致一些图形问题并最终导致控件完全停止工作,因此您甚至无法滚动光标。仅供参考。
    • 从工作线程更新控件时非常标准的故障模式。
    • 汉斯,我对这些东西比较陌生。你在说/暗示什么?在上述类扩展之后,对文本框的更新应该在单独的线程上完成吗?感谢您的宝贵时间。
    【解决方案2】:

    我没有积累足够的积分来修改汉斯的推荐。所以我添加了这个 Answer 来说明可能需要通过调用 InvalidateRect 来请求重绘。一些 Begin/End Update 实现会在更新锁的最终释放时自动执行此操作。同样在 .Net 中,可以调用 Control.Invalidate() 来调用本机 InvalidateRect 函数。

    MSDN:最后,应用程序可以调用 InvalidateRect 函数来重新绘制列表框。

    WM_SETREDRAW

    【讨论】:

    • 我遇到了与 Killercam 描述的相同的问题 - 在重新启用绘图后添加一个 Invalidate() 修复了它。
    【解决方案3】:

    完成您正在尝试做的事情的最佳选择是创建一个多线程应用程序。您需要创建一个线程来检查您的列表中的文本。该线程会将它找到的任何实例放入队列中。您还需要创建另一个线程来实际突出显示单词。因为您需要使用 BeginInvoke() 和 Invoke() 来更新 UI,所以您需要确保限制调用它的速率。我不会超过每秒 20 次。为此,您可以使用如下代码:

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

    此线程将检查您的队列是否有需要突出显示或重新突出显示的单词,并将不断检查队列是否有任何新更新。希望这是有道理的!

    【讨论】:

    • 谢谢,如果我找不到问题的答案,我可能会尝试这样做。如果我可以在突出显示时禁用重绘,那么我现在正在做的事情就完美了。
    • 是的,我知道这不是您问题的直接答案,但我认为您会很感激您的意见:) 这种方法是 Microsoft Word 等程序的工作原理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多