更新期间停止文本框闪烁

Posted

技术标签:

【中文标题】更新期间停止文本框闪烁【英文标题】:Stopping TextBox flicker during update 【发布时间】:2010-12-05 17:41:42 【问题描述】:

我的 WinForms 应用程序有一个文本框,我将其用作日志文件。我正在使用TextBox.AppendText(string); 附加文本而没有闪烁的表单,但是当我尝试清除旧文本时(因为控件的 .Text 属性达到 .MaxLength 限制),我得到了可怕的闪烁。

我使用的代码如下:

public static void AddTextToConsoleThreadSafe(TextBox textBox, string text)

    if (textBox.InvokeRequired)
    
        textBox.Invoke(new AddTextToConsoleThreadSafeDelegate(AddTextToConsoleThreadSafe), new object[]  textBox, text );
    
    else
    
        // Ensure that text is purged from the top of the textbox
        // if the amount of text in the box is approaching the
        // MaxLength property of the control

        if (textBox.Text.Length + text.Length > textBox.MaxLength)
        
            int cr = textBox.Text.IndexOf("\r\n");
            if (cr > 0)
            
                textBox.Select(0, cr + 1);
                textBox.SelectedText = string.Empty;
            
            else
            
                textBox.Select(0, text.Length);
            
        


        // Append the new text, move the caret to the end of the
        // text, and ensure the textbox is scrolled to the bottom

        textBox.AppendText(text);
        textBox.SelectionStart = textBox.Text.Length;
        textBox.ScrollToCaret();
    

有没有一种更简洁的方法可以从控件顶部清除不会导致闪烁的文本行?文本框没有 ListView 所具有的 BeginUpdate()/EndUpdate() 方法。

TextBox 控件甚至是最适合控制台日志的控件吗?

编辑:TextBox 闪烁似乎是文本框向上滚动到顶部(当我清除控件顶部的文本时),然后它立即向下滚动到底部。 - 这一切都发生得很快,所以我只看到反复闪烁。

我也刚刚看到 this question,建议使用 ListBox,但我不知道这是否适用于我的情况,因为(在大多数情况下)我收到的文本ListBox 一次一个字符。

【问题讨论】:

可能需要将“if”更改为“while”——以防删除第一行文本不足以让新文本适合 TextBox。 这篇文章有更多关于这个的信息 - ***.com/questions/1333393/… 其实它有一个非常清晰的解决方案,双缓冲不适用于文本框,所以你应该手动做...... Vinko,请发表以上评论作为答案,我可以接受。 只需使用 RichTextBox。无闪烁。将 DetectUrls 和 ShortcutsEnabled 属性设置为 FALSE 以提高与 TextBox 的兼容性。真的很好用。 【参考方案1】:

Mathijs 的答案对我有用。我已经稍微修改了它,以便可以与任何控件一起使用 - 控件扩展:

namespace System.Windows.Forms

    public static class ControlExtensions
    
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool LockWindowUpdate(IntPtr hWndLock);

        public static void Suspend(this Control control)
        
            LockWindowUpdate(control.Handle);
        

        public static void Resume(this Control control)
        
            LockWindowUpdate(IntPtr.Zero);
        

    

所以你需要做的就是:

myTextBox.Suspend();
// do something here.
myTextBox.Resume();

效果很好。所有闪烁停止。

【讨论】:

仅供参考,如果// do something here. 是一个调整大小事件,这将无法正常工作。 "LockWindowUpdate 不适用于一般用途的窗口重绘抑制。使用 WM_SETREDRAW 消息禁用特定窗口的重绘。" docs.microsoft.com/en-us/windows/desktop/api/winuser/…【参考方案2】:

我在网上找到了一个解决方案:

    [System.Runtime.InteropServices.DllImport("user32.dll")]

    public static extern bool LockWindowUpdate(IntPtr hWndLock);

    internal void FillTB(TextBox tb, string mes) 
    
       try
       
          LockWindowUpdate(tb.Handle);

          // Do your thingies with TextBox tb
       
       finally
       
          LockWindowUpdate(IntPtr.Zero);
       
    

【讨论】:

谢谢,对我也有用——事实上我使用了 mkaj 的扩展【参考方案3】:

您是否在主窗口上设置了双缓冲?

InitializeComponent 调用后构造函数中的这段代码将添加双缓冲并可能减少闪烁。

this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer,true);

【讨论】:

这个我试过了,不幸的是它没有任何区别。 这无济于事; TextBox 本质上是闪烁的,你能希望的最好的就是减少它。 ***.com/questions/1333393/…【参考方案4】:

我发现使用 SelectedText = text 会显着减少闪烁。对于非常快速的更新,闪烁将仅针对新文本进行本地化,您不会因滚动条四处跳动而出现任何奇怪的行为。

void UpdateTextBox(string message)

   myTextBox.SelectionStart = myTextBox.Text.Length;
   myTextBox.SelectedText = message;

您还可以使用它来覆盖以前编写的文本——例如更新计数器或下载百分比时需要:

void UpdateTextBox(string message, int jumpBack)

   myTextBox.SelectionStart = Math.Max(myTextBox.Text.Length - jumpBack, 0);
   myTextBox.SelectionLength = jumpBack;
   myTextBox.SelectedText = message;

除此之外,似乎没有任何简单的方法可以减少 .NET TextBox 中的闪烁。

【讨论】:

【参考方案5】:

问题是您一次又一次地快速添加(删除)一个字符。一种解决方案是在添加字符时对其进行缓冲,并以更大的间隔(无论字符数量如何)更新文本框,例如每 250 毫秒。

这需要:

在其中添加字符的数组或堆栈 拥有一个计时器,该计时器将调用一个委托,该委托实际上会使用存储在堆栈中的字符进行更新

另一种选择是每 250 毫秒和 100 个字符都使用一次,无论先发生什么。但这可能会使代码更加复杂而没有任何实际好处。

【讨论】:

这不仅会增加闪烁率吗? 这给了我一个想法,但我可以使用它,直到找到更好的解决方案。当控件填满时,我现在清除大约 20% 的内容(我可以承受掉 20%,尽管我宁愿让它正常工作)。这样,当控件已满时,您就不会经常看到闪烁,您不太可能注意到它,因为闪烁可能每小时只发生一次,而不是每秒发生几次。 这些时期没有太多想法,我必须承认:) 尽管我接受了这个问题,但我找到的最佳答案是 Vinko 在回复我原来的问题时的评论。 请大家使用 RichTextBox。它没有闪烁。将 DetectUrls 和 ShortcutsEnabled 属性设置为 FALSE 以提高与 TextBox 的兼容性。它确实很好用,否则您需要覆盖 TextBox 的 PAINT 消息并自己控制剪切/验证区域。【参考方案6】:

您是否尝试过所有更新操作的 SuspendLayout() / ResumeLayout()?

您也可以在文本框上调用 Clear(),然后重新分配截断的文本。

如果您尝试实现某种日志文件查看器,则可以使用 ListBox。

【讨论】:

我尝试了双缓冲和 SuspendLayout() / ResumeLayout(),不幸的是它们似乎没有任何区别。我已经更新了关于使用 ListBox 的问题,但我不确定它是否会起作用,因为我(通常)一次添加一个字符。 您可以更新列表框中的最后一项以添加一个字符,如果该行已满,您可以添加一个新的列表框项。 您是否在 SuspendLayout()/ResumeLayout() 中包含 所有 操作,包含 ScrollToCaret 调用? 是的,我将suspend/resume作为方法调用部分的第一条和最后一条语句。 另外,再想一想,我认为列表框不会起作用,因为用户需要能够将文本复制到剪贴板。他们需要能够通过点击/拖动来选择他们想要复制的文本。

以上是关于更新期间停止文本框闪烁的主要内容,如果未能解决你的问题,请参考以下文章

visual c# 表单更新导致闪烁

文本框光标不闪烁

在输入/复制到富文本框时防止闪烁

从 .NET 文本/闪烁框读取行而不使用太多内存?

如何在 onKeyPress 期间获取输入文本框的文本?

当另一个文本框在报告中增加其大小时如何停止增加文本框的大小