views:

2294

answers:

3

There are plenty of articles addressing flicker in Windows Forms. The majority recommend setting DoubleBuffered = true or setting a bunch of ControlStyle flags. However, none of these help reduce a TextBox flickering.

Here are a couple of related questions:

To reproduce the issue, create a new WinForms project, add a TextBox, enable multi-line, disable word-wrap, add a bunch of text, set Anchor to Left+Right+Top+Bottom. Now run and resize. The text flickers. For text boxes inside a couple of nested TableLayoutPanels the flicker on resize is even worse.

Applying the solutions proposed in the above questions at best do not fix the flicker; if I get experimental and set the protected ControlStyle on TextBox I can break it completely (by enabling UserPaint) but not eliminate the flicker.

So, is there any way at all to fix the flickering of the text in a TextBox?

+1  A: 

We have encountered same kind of problem in past and it comes out to be use of excessive docking and table layout panels. I will suggest, if possible, try to re-construct the UI with minimal use of docking (as table layout panel also uses docking internally).

Ramesh Soni
I've also observed that the flicker gets worse as the number of layout-related controls increases. Unfortunately there's still flicker even with the most minimal layout necessary to achieve basic resizing - like in my example.
romkyns
+1  A: 

In Windows Forms the DoubleBuffered property does not affect child controls such as text boxes. Instead it affects just the form or Panel it is set for.

If you want double buffering for child elements on a form, you will need to implement manual double buffering.

Bob Powell has written a good article (and others) on how to do this.

Also, from a forum answer Bob also says:

The ownership of a window means that they will flicker uncontrollably because you cannot double-buffer outside of the target windows area. A panel with child controls cannot be made to double buffer itself and it's children for example.

The only way to do this correctly is to create a single control that does all the drawing using a form of retained mode graphics system.

Therefore, to get flicker free textbox resize using manual double buffering you would need to somehow render the textbox to your back buffer and then display it as part of the buffered update. If even possible, I do not expect this would be easy.

[Update]

Some other answers have said this is a problem with Windows Forms specifically. This is not correct, it is actually deeper then that and is caused by Windows GDI. As an example, open Notepad / Wordpad etc and paste a large chunk of text, resize the window and notice the same flickering issue.

Here is a basic solution I used years ago to do something similar. It is a simple form containing a multiline textbox and a custom class inheriting from Panel. Both controls have the same location and size. It uses the Forms ResizeBegin and ResizeEnd to show the panel when resizing, and the textbox otherwise. It's not perfect but it does eliminate the flickering.

   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);
    }
}
Ash
Thanks. The article might be useful to people unfamiliar with double buffering techniques, but doesn't really help address the question. Regarding window ownership - I am getting the feeling that the native TextBox on windows is inherently flickery and this is unfixable without extreme hacks...
romkyns
I've added an update to my question. Unfortunately the answer to your question is simply "you can't", howver I thought some background info would be useful.
Ash
On checking some of my old code I see it's simple to render a textbox without flicker, using the DrawToBitmap() method onto an in- memory bitmap. But this would simply create an on-screen image of your text box that never flickers when resized but is utterly useless as it doesnt allow you to enter text.
Ash
Accepted for this insight: "As an example, open Notepad / Wordpad etc and paste a large chunk of text, resize the window and notice the same flickering issue." - indeed, the built-in text box appears to be inherently flickery.
romkyns
+2  A: 

The problem really is that the WinForms is not really that well implemented imho. This should work flicker-free out of the box. When you resize a window, Windows (the OS) sends an event to the relevant controls that part of their visible area has been invalidated. It directly tells the application what part of the screen needs to be redrawn, so the controls don't need to do a complete redraw. This is natively supported in the Win32 API.

But the WinFowms implementation does not implement partial update (or at least not very well - every winform application I have worked on had flickering problems), so every time you resize the screen, the entire control is redrawn, leading to flickering.

I know this will not help you out, just an explanation why you see flickering in your application, where for example visual studio (which is not a WinForms application) resizes smoothly.

Edit: So what you could do is override the windows event handling of each control on the page, to make sure that it only performs partial updates. But that means making your application dependent on the actual Win32 API, i.e. listening for specific windows events, etc. I think that it would be a nightmare to implement, and I wouldn't do it.

Pete