如何防止Windows窗体中的文本框在调整大小时闪烁?

19
有很多关于Windows Forms中闪烁问题的文章。其中大部分建议设置DoubleBuffered = true或设置一堆ControlStyle标志。然而,这些方法都不能减少TextBox的闪烁。
以下是一些相关问题: 要重现此问题,请创建一个新的WinForms项目,添加一个TextBox,启用多行,禁用单词换行,添加一些文本,将Anchor设置为左+右+上+下。现在运行并调整大小,文本就会闪烁。对于一些嵌套在一对TableLayoutPanel中的文本框,在调整大小时的闪烁甚至更严重。
尝试以上提出的解决方案最好也无法解决闪烁问题;如果我实验性地在TextBox上设置受保护的ControlStyle,我可以通过启用UserPaint来完全破坏它,但无法消除闪烁。
那么,有没有任何方法可以修复TextBox文本的闪烁问题呢?

1
有趣的帖子,我有一个标签控件类似的问题,但是我读到DoubleBuffer应该只与AllPaintInWmPaint一起设置,而应该只使用UserPaint来设置,所以我没有使用双缓冲... - Ian
@romkyns,我已经更新了我的答案,并提供了我过去使用过的解决方案。 - Ash
4个回答

16

我通常使用一个RichTextBox而不是多行文本框。 通过将DetectUrls和ShortcutsEnabled属性设置为false,RTB的行为与文本框非常相似,而且...它是无闪烁的


这绝对是最快,最简单的解决方案。我使用基于TextBox的自定义控件,只需将其父对象更改为RichTextBox - 问题解决了。 - Andy
为什么这个答案没有被采纳呢?它完全准确,谢谢!(当然,对于不是文本框的子控件的闪烁问题无法解决,但那也不是问题所在。) - neminem
太遗憾了,它不支持UWP :( - kayleeFrye_onDeck

5
在Windows Forms中,DoubleBuffered属性不会影响文本框等子控件。它只会影响设置了该属性的窗体或面板。
如果您想在窗体上实现子元素的双缓冲,您需要手动实现双缓冲。
Bob Powell撰写了一篇关于如何做到这一点的好文章(以及其他文章)。
此外,根据Bob在论坛答案中的说法:
拥有一个窗口意味着它们将无法控制地闪烁,因为您无法在目标窗口区域之外进行双缓冲。例如,带有子控件的面板不能使自己和其子控件都进行双缓冲。
正确的方法是创建一个单独的控件,使用保留模式图形系统进行所有绘图。
因此,要使用手动双缓冲获得无闪烁的文本框调整大小,您需要以某种方式将文本框呈现到后台缓冲区,并将其作为缓冲更新的一部分显示。如果可能的话:我不认为这会很容易。
[更新]
其他答案称这是Windows Forms特有的问题。这是不正确的,实际上这是由Windows GDI引起的更深层次的问题。例如,打开记事本/字处理软件等并粘贴大块文本,调整窗口大小,注意到相同的闪烁问题。
以下是我多年前用来做类似事情的基本解决方案。它是一个包含多行文本框和从面板继承的自定义类的简单表单。两个控件具有相同的位置和大小。它使用Forms ResizeBegin和ResizeEnd在调整大小时显示面板,否则显示文本框。它不是完美的,但确实消除了闪烁。
   public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        Bitmap bm = null;

        private void textBox1_Resize(object sender, EventArgs e)
        {

            Graphics g = textBox1.CreateGraphics();

            if (g.VisibleClipBounds.IsEmpty == false)
            {
                bm = new Bitmap((int)g.VisibleClipBounds.Width, (int)g.VisibleClipBounds.Height);

                textBox1.DrawToBitmap(bm, new Rectangle(0, 0, (int)g.VisibleClipBounds.Width, (int)g.VisibleClipBounds.Height));

            }

            g.Dispose();


        }

        private void panelDB1_Paint(object sender, PaintEventArgs e)
        {
            if (bm != null)
            {
                e.Graphics.DrawImageUnscaled(bm, 0, 0,bm.Width,bm.Height );
            }
        }

        private void Form1_ResizeBegin(object sender, EventArgs e)
        {
            panelDB1.BringToFront();  
        }

        private void Form1_ResizeEnd(object sender, EventArgs e)
        {
            panelDB1.SendToBack();   
        }
}

class PanelDB : Panel
{
    public PanelDB()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer,true);       
        //this.DoubleBuffered = true; 
    
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
    }
}

谢谢。这篇文章可能对不熟悉双缓冲技术的人有用,但并没有真正帮助解决问题。关于窗口所有权 - 我感觉Windows上的本地TextBox本质上是闪烁的,这是无法通过极端的黑客行为来修复的... - Roman Starkov
我已经更新了我的问题。不幸的是,对于你的问题,答案很简单:“你不能”,但我认为提供一些背景信息可能会有用。 - Ash
在检查我的一些旧代码时,我发现使用DrawToBitmap()方法渲染文本框而不会出现闪烁是很简单的。但这只会创建一个屏幕上的图像,当调整大小时永远不会闪烁,但它是完全无用的,因为它不允许您输入文本。 - Ash
接受此见解:“例如,打开记事本/Wordpad等,粘贴大块文本,调整窗口大小并注意到相同的闪烁问题。” - 实际上,内置的文本框似乎天生就有闪烁问题。 - Roman Starkov

0

我们过去遇到过同样的问题,结果发现使用了过多的停靠和表格布局面板。我建议,如果可能的话,尽量使用最少的停靠重新构建UI(因为表格布局面板也在内部使用停靠)。


我也观察到,随着与布局相关的控件数量的增加,闪烁现象会变得更严重。不幸的是,即使在实现基本调整大小所需的最小布局(就像在我的示例中一样),仍然会出现闪烁。 - Roman Starkov

-3

函数 LockWindow() 作为长整型返回值 控件发送 ghDlg,%TEXT_UPPER,%WM_SETREDRAW,0,0 清除缓冲区 结束函数

函数 UnlockWindow() 作为长整型返回值 清除缓冲区 控件发送 ghDlg,%TEXT_UPPER,%WM_SETREDRAW,1,0 结束函数


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接