在键入/复制到富文本框时防止闪烁
出于颜色编码的原因,我需要一个系统,我经常将字符串粘贴到富文本框(而不是默认的标准输入)。不幸的是,它经常会导致闪光,尤其是在按住某个键的情况下。
RTB 似乎不支持双缓冲,但我不确定这是否有帮助。覆盖绘制事件似乎也无效。在研究了网络之后,到目前为止我发现的最好的“解决方案”是使用本机 Windows 互操作(LockWindowUpdate 等)。这解决了超出滚动点打字绝对可怕的情况。不幸的是,现在仍然存在(较小的)闪烁。
下面的代码可以立即编译(只需创建一个控制台项目并引用 System.Windows.Forms 和 System.Drawing)。如果这样做,请按一个键,并按住该键大约 10 行。如果这样做,您会发现越来越多的闪烁出现。你输入的越多,闪烁就会变得越严重。
using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace FlickerTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } [DllImport("user32.dll")] public static extern bool LockWindowUpdate(IntPtr hWndLock); private void rtb_TextChanged(object sender, EventArgs e) { String s = rtb.Text; LockWindowUpdate(rtb.Handle); rtb.Text = s; rtb.Refresh(); ////Forces a synchronous redraw of all controls LockWindowUpdate(IntPtr.Zero); } } ////////////////////////////////////////////////// // Ignore below: static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } partial class Form1 { private System.ComponentModel.IContainer components = null; protected override void Dispose(bool disposing) { if (disposing && (components != null)) components.Dispose(); base.Dispose(disposing); } #region Windows Form Designer generated code private void InitializeComponent() { 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); // Form1 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 private System.Windows.Forms.RichTextBox rtb; } }
For colour coding reasons, I need a system whereby I often paste a string to a rich text box (instead of the default standard typing). Unfortunately, it often causes a flash, especially if you keep a key held down.
The RTB doesn't seem to support double buffering, but I'm not sure if that would help anyway. Over-riding the on-paint event also appears ineffective. After researching the web, the best 'solution' I've found so far is to use native Windows interop (LockWindowUpdate etc.). This cured the situation where typing beyond the scroll point was absolutely horrible. Unfortunately, there's still a (lesser) flicker generally now.
The below code is immediately compilable (just create a console project and reference System.Windows.Forms and System.Drawing). If you do, press a key, and keep it held down for say 10 lines worth. If you do, you'll notice more and more flicker cropping up. The more you type, the worse the flicker will become.
using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace FlickerTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } [DllImport("user32.dll")] public static extern bool LockWindowUpdate(IntPtr hWndLock); private void rtb_TextChanged(object sender, EventArgs e) { String s = rtb.Text; LockWindowUpdate(rtb.Handle); rtb.Text = s; rtb.Refresh(); ////Forces a synchronous redraw of all controls LockWindowUpdate(IntPtr.Zero); } } ////////////////////////////////////////////////// // Ignore below: static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } partial class Form1 { private System.ComponentModel.IContainer components = null; protected override void Dispose(bool disposing) { if (disposing && (components != null)) components.Dispose(); base.Dispose(disposing); } #region Windows Form Designer generated code private void InitializeComponent() { 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); // Form1 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 private System.Windows.Forms.RichTextBox rtb; } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您不需要为每次按键刷新 RTB,这是在不调用任何 Application.DoEvents() 的情况下处理 TextChanged 时发生的情况。
我发现获得可接受性能的最佳方法是处理 KeyDown 事件。如果键码(带修饰符)是可打印字符,我会启动一个计时器,其 Tick 事件处理程序会在 100 毫秒后检查 Richtextbox 的文本。
因为当 KeyDown 事件被触发时,键入的字符尚未打印,您实际上可以通过设置 SelectionColor 属性在此事件处理程序中修改此字符的文本颜色(无需更改代码中的选择),因此您不需要需要冻结控制。不要尝试在这方面做太多处理器密集型的事情,因为你会遇到响应问题。
最后,修改 RichTextBox 行为是一个螺旋式的过程,一旦偏离规范,您将开始使用自定义代码来执行通常使用控件自身功能执行的各种任务(例如复制/粘贴、撤消/重做和滚动) 。然后,您可以决定是继续自己还是去找第三方编辑。另请注意,虽然您已经开始使用 Windows API,但还会有更多这样的内容,特别是对于滚动、打印和绘制事件,尽管有很好的在线资源记录了这些事件。
不要因此而泄气,只需注意如果您的应用程序需求增长,未来可能会发生什么。
You shouldn't need to refresh the RTB for each key-press which is what happens when you handle TextChanged without any Application.DoEvents() call.
I've found the best way to get acceptable performance is to process the KeyDown event instead. If the keycode (with modifier) is a printable character I start a timer whose Tick event handler checks the text of the richtextbox after say 100ms.
Because when the KeyDown event is fired, the typed char hasn't been printed, you can actually modify text-color for this char within this event handler by setting the SelectionColor property (without changing the selection in code), so you don't need to freeze the control. Don't try doing too much processor-intensive stuff in this though as you'll get responsiveness issues.
Finally, modifying RichTextBox behaviour is a sprial, as soon as you deviate from the norm you will start to use custom code for all sorts of tasks (like copy/paste undo/redo and scrolling) that would normally be performed using the controls own features. You then have a decision to make on whether to continue yourself or go for a 3rd party editor. Note also that though you've started using the Windows API already, there will be much more of this, for scrolling, printing and paint events in particular, though there are good online resources documenting these.
Don't let this discourage you, just be aware what the future may hold if your application requirements grow.
我最近遇到了类似的问题。我选择的方法是向richtextbox添加扩展方法。我喜欢这种方法,因为它干净、封闭且易于重复使用。
这使得暂停重画就像
恢复一样简单
I ran into a similar problem recently. The method I chose was to add extension methods to the richtextbox. I like this approach because it's clean, contained, and easily reusable.
This makes pausing the redraw is as simple as
and resuming