views:

884

answers:

5

Hello,

I know that double-buffering is an often-talked subject but no matter how much I searched and tried different approaches, I still can't get the control to re-draw itself without a flicker. Here's my code:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Emgu.UI
{
    public class DoubleBufferedPictureBox : Control
    {
        const BufferedGraphics NO_MANAGED_BACK_BUFFER = null;

        BufferedGraphicsContext GraphicManager;
        BufferedGraphics ManagedBackBuffer;

        public Bitmap Bitmap { get; set; }
        public Rectangle DrawRectangle { get; set; }

        public DoubleBufferedPictureBox()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            GraphicManager = BufferedGraphicsManager.Current;
            GraphicManager.MaximumBuffer =
                   new Size(Width + 1, Height + 1);
            ManagedBackBuffer =
                GraphicManager.Allocate(CreateGraphics(),
                                               ClientRectangle);

            Resize += DoubleBufferedPictureBox_Resize;
        }

        void DoubleBufferedPictureBox_Resize(object sender, EventArgs e)
        {
            if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
                ManagedBackBuffer.Dispose();

            GraphicManager.MaximumBuffer =
                  new Size(Width + 1, Height + 1);

            ManagedBackBuffer =
                GraphicManager.Allocate(CreateGraphics(),
                                                ClientRectangle);
            Refresh();
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            ManagedBackBuffer.Graphics.DrawImage(Bitmap, DrawRectangle);
            ManagedBackBuffer.Render(pe.Graphics);
        }

    }
}

Any ideas?

A: 

Have you tried setting the controls DoubleBuffered property to true?

scottm
Yes I have. Tried again now for good measure - same results...
What about in your SetStyles method? ex: SetStyles(ControlStyles.AllPaintingInWmPaint | ControlSyles.DoubleBuffer, true);
scottm
ControlStyles.DoubleBuffer is from 1.1 - I used OptimizedDoubleBuffer from 2.0 with no effect...
well then... Are you sure you are rendering the buffer and then copying the graphics over? it looks like you may be just using the buffer to render directly to the Graphics property, rather than buffering the render graphic and then copying it over.
scottm
I've even tried doing this manually. That is - I create a 32-bpp Bitmap the ClientSize of a PictureBox with DoubleBuffered = true and then set it to the PictureBox.Image - it still flickers darn it...
if you set the OptimizedDoubleBuffer flag you must also set the UserDraw flag. Have you tried that?
Ed Swangren
A: 

Is painting in the parent control(Form?) also double-buffered?

Your custom control may be painted without flicker but further when the parent control is painted and is not double-buffered, it's flickering.

You may be double-buffering the wrong thing.

arul
Just tried that also, no effect.
Can you upload somewhere the code you're trying so that we can tinker with it ?
arul
Hm - I'm trying to re-produce this and it's proving hard - the PictureBox is contained within another Control that I haven't designed myself and that's where the flicker seems to come from - I'm investigating more now.
A: 

I think the problem is in your OnPaint method, from MSDN, when you call your ManagedBackBuffer.Render method, you should pass CreateGraphics() not the Graphics property:

scottm
Nothing :( How hard can it be, right?
+2  A: 

Oh my God...

Like I said in my comments, the pictureBox is contained within another Control that I didn't write (but have the source to). It turns out that the flicker is caused by these two lines:

if (pictureBox.Width != _displayedImage.Width) pictureBox.Width = _displayedImage.Width;
if (pictureBox.Height != _displayedImage.Height) pictureBox.Height = _displayedImage.Height;

I think that's because the PictureBox is actually docked in the parent control...

Any way, thanks for all your responses.

You probably want to do those both at once -- i.e. pictureBox.Size = _displayedImage.Size.
mquander
+2  A: 

Are you using .Net 2.0 ? If so, I found that the following windows styles do the trick, even with old stuborn Win32 controls embeded, and it's fast too!

// Note the >>> Optimized <<< DoubleBuffer
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);

Make sure you always draw from inside a WM_PAINT message, else it will flicker. You don't need anything else, everything is automatic.

Coincoin