在输入/复制到富文本框时防止闪烁
Posted
技术标签:
【中文标题】在输入/复制到富文本框时防止闪烁【英文标题】:Prevent flashing whilst typing/copying to rich text box 【发布时间】:2011-09-08 04:03:51 【问题描述】:出于颜色编码的原因,我需要一个系统,我经常将字符串粘贴到富文本框中(而不是默认的标准类型)。不幸的是,它通常会导致闪烁,尤其是在您按住某个键的情况下。
RTB 似乎不支持双缓冲,但我不确定这是否会有所帮助。覆盖 on-paint 事件也似乎无效。在研究了网络之后,到目前为止我发现的最好的“解决方案”是使用本机 Windows 互操作(LockWindowUpdate 等)。这解决了超出滚动点打字绝对可怕的情况。不幸的是,现在通常仍然存在(较小的)闪烁。
下面的代码可以立即编译(只需创建一个控制台项目并引用 System.Windows.Forms 和 System.Drawing)。如果你这样做了,请按下一个键,并按住它说 10 行。如果你这样做,你会注意到越来越多的闪烁出现。输入越多,闪烁越严重。
使用系统; 使用 System.Windows.Forms; 使用 System.Runtime.InteropServices; 命名空间闪烁测试 公共部分类Form1:表格 公共表格1() 初始化组件(); [DllImport("user32.dll")] public static extern bool LockWindowUpdate(IntPtr hWndLock); 私人无效rtb_TextChanged(对象发送者,EventArgs e) 字符串 s = rtb.Text; LockWindowUpdate(rtb.Handle); rtb.Text = s; rtb.Refresh(); ////强制同步重绘所有控件 LockWindowUpdate(IntPtr.Zero); ///////////////////////////////////////// // 忽略下面: 静态类程序 [STA线程] 静态无效 Main() Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); 应用程序.运行(新 Form1()); 部分类Form1 私有 System.ComponentModel.IContainer 组件 = null; 受保护的覆盖无效处置(布尔处置) if (disposing && (components != null)) components.Dispose(); base.Dispose(处置); #region Windows 窗体设计器生成的代码 私人无效初始化组件() this.rtb = new System.Windows.Forms.RichTextBox(); this.SuspendLayout(); // rtb this.rtb.BackColor = System.Drawing.Color.Black; this.rtb.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.rtb.ForeColor = System.Drawing.SystemColors.Window; this.rtb.Location = new System.Drawing.Point(24, 20); this.rtb.Name = "rtb"; this.rtb.Size = new System.Drawing.Size(609, 367); this.rtb.TabIndex = 0; this.rtb.Text = ""; this.rtb.TextChanged += new System.EventHandler(this.rtb_TextChanged); //表格1 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1088, 681); this.Controls.Add(this.rtb); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); #endregion 私有 System.Windows.Forms.RichTextBox rtb;【问题讨论】:
【参考方案1】:您不需要为每次按键刷新 RTB,当您在没有任何 Application.DoEvents() 调用的情况下处理 TextChanged 时会发生这种情况。
我发现获得可接受性能的最佳方法是改为处理 KeyDown 事件。如果键码(带修饰符)是可打印字符,我会启动一个计时器,其 Tick 事件处理程序会在 100 毫秒后检查 Richtextbox 的文本。
因为当 KeyDown 事件被触发时,键入的字符还没有被打印出来,你实际上可以通过设置 SelectionColor 属性在这个事件处理程序中修改这个字符的文本颜色(不改变代码中的选择),所以你不需要冻结控件。不过,不要尝试在这方面做太多处理器密集型的事情,因为你会遇到响应问题。
最后,修改 RichTextBox 行为是一种螺旋式的行为,一旦您偏离规范,您就会开始使用自定义代码来执行通常使用控制自己的功能。然后,您可以决定是继续自己还是选择第 3 方编辑。另请注意,尽管您已经开始使用 Windows API,但还会有更多此类功能,尤其是用于滚动、打印和绘制事件,尽管有很好的在线资源记录了这些内容。
不要让这让您灰心,只要知道如果您的应用程序需求增长,未来可能会发生什么。
【讨论】:
只是为了先让路; 100 毫秒对我的需求来说太慢了——我需要即时反馈。它需要在用户按下某个键的同时出现在视觉上。我刚刚发现,即使是文本更改事件或 keydown/press 事件中的单个命令 rtf.Refresh() 或 rtb.Invalidate() 仍然会导致闪烁。 @Daniel White。 100 毫秒是计时器间隔,在此之后您开始正确解析,而不是键盘到屏幕的响应时间会少很多(比如 10 毫秒),即使 200 毫秒相当于每秒 5 个字符,这大约是速度打字员的最大值。使用在每次击键时重置的计时器可确保在按下并按住某个键时,您仅在键被抬起后才解析。为防止闪烁,请尝试 StopRepaint answer here。【参考方案2】:我最近遇到了类似的问题。我选择的方法是在richtextbox 中添加扩展方法。我喜欢这种方法,因为它干净、包含且易于重复使用。
这使得暂停重绘就像
一样简单richtextbox.SuspendDrawing();
并继续
richtextbox.ResumeDrawing()
public static class RichTextBoxExtensions
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private const int WM_SETREDRAW = 0x0b;
public static void SuspendDrawing(this System.Windows.Forms.RichTextBox richTextBox)
SendMessage(richTextBox.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
public static void ResumeDrawing(this System.Windows.Forms.RichTextBox richTextBox)
SendMessage(richTextBox.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
richTextBox.Invalidate();
【讨论】:
不幸的是,这似乎并不总是有效。使用此解决方案时,我仍然会看到明显的闪烁。 @fakedad 确保在恢复之前完成所有更新。此外,您可能还需要设置 RichTextbox 以允许双缓冲。以上是关于在输入/复制到富文本框时防止闪烁的主要内容,如果未能解决你的问题,请参考以下文章