Question Is everything in a PictureBox drawn at the same time?

AdamFas91

New member
Joined
Jan 5, 2023
Messages
2
Programming Experience
3-5
Hey all,

Been working on a project for a bit that has a pictureBox set to re-draw on a timer. Essentially, it draws a flashing black and white square on one side of the screen and a reversing checkerboard (black checks to white and white checks to black) on the other side. The timer triggers either every half-second or every quarter-second. What should ideally be happening is that the checkerboard reverses at the exact same time as the square on the other side changes from white-to-black (or vice versa). However, I am drawing the checkerboard programmatically (one check at a time) to allow for different check densities, and I think the checkerboard ends up drawing just slightly after the square flips (maybe by 20 milliseconds). This is a very time-sensitive program, so any delay between the square and the checkerboard is unacceptable. Is there a way to ensure that everything in the pictureBox is drawn at the same time? I tried doubleBuffered, but I think I'm still getting a delay. Thanks in advance!
C#:
private void drawBoard(Graphics graphics, int checknum, bool dark, bool left, int tickCount)
        {
            width = (sWidth / 2) / checknum;
            height = (sWidth / 2) / checknum;

                // Checks for left or right side
                if (left == true)
                {
                    // Draw checkerboard for left side
                    for (int i = 0; i < checknum; i++)
                    {
                        dark = !dark;
                        for (int j = 0; j < checknum; j++)
                        {
                            dark = !dark;
                            if (dark == true)
                            {
                                // This is bright
                                brush = whiteBrush;
                            }
                            else
                            {
                                // This is dark
                                brush = blackBrush;
                            }
                            // Checkerboard left edge
                            x = width * i;
                            y = (height * j) + (sHeight - (sWidth / 2)) / 2;
                            // Draw rectangle to screen.
                            graphics.DrawRectangle(blackPen, x, y, width, height);
                            graphics.FillRectangle(brush, x, y, width, height);
                        }
                    }
                }
                else
                {
                    // Draw checkerboard for right side
                    for (int i = 0; i < checknum; i++)
                    {
                        dark = !dark;
                        for (int j = 0; j < checknum; j++)
                        {
                            dark = !dark;
                            if (dark == true)
                            {
                                // This is bright
                                brush = whiteBrush;
                            }
                            else
                            {
                                // This is dark
                                brush = blackBrush;
                            }
                            // Checkerboard right edge
                            x = width * i + (sWidth / 2);
                            y = (height * j) + (sHeight - (sWidth / 2)) / 2;
                            // Draw rectangle to screen.
                            graphics.DrawRectangle(blackPen, x, y, width, height);
                            graphics.FillRectangle(brush, x, y, width, height);
                        }
                    }
                }
            }

            // Draw flashing rectangle
            // NOTE!  We are moving this to later as a check!  Possibility that the checkerboard creation is taking such a long time
            // that flashing rectangle was being drawn FAR before checkerboard.  May need to find a better way to improve checkerboard
            // creation... try to switch to a simple bitmap that we can flip and tile as needed?
            if (dark == true)
            {
                fill = whiteBrush;
            }
            else
            {
                fill = blackBrush;
            }
            graphics.DrawRectangle(blackPen, trig);
            graphics.FillRectangle(fill, trig);
        }
 
Is your draw board being called by the paint event of the picture box, and are you passing in the Graphics object from that paint event?

If not then double buffering is not actually having any effect because you are likely drawing straight to the GDI context of the screen.
 
Thanks for the help so far - here's my paint event code, so it should be passing the Graphics object.

private void pictureBox1_Paint(object sender, PaintEventArgs paintEvent)
{
Graphics g = paintEvent.Graphics;
drawBoard(g, pattern, dark, left, tickCount);
}

I suppose I'm not entirely sure where to place the doubleBuffered variable, right now I have it in the form_Load method. However, I noticed that I had it as this.DoubleBuffered = true, which would affect the form itself. I'm not sure if that would inherently affect the pictureBox as well?

I use pictureBox.Invalidate() to redraw the pictureBox on the timer tick after switching the "dark" variable, is there a better way to redraw the pictureBox that I can use?
 
Last edited:
Ideally, you should have turned of DoubleBuffered in the designer property pane, and that would have set the property to true inside the InitializeComponent(). Otherwise, you'll want to set the property as soon after InitializeComponent () before the window tries to load or show any of its controls.

It looks like you have the paint event set-up correctly. Does your timer event just call invalidate, or does it also force a call to your draw board? Hopefully all you do is change your variables, and call invalidate.
 
Not exactly your code, but the following draws a flashing checker board pattern with double buffering on. I'm not seeing odd drawing artifacts that would indicate that double buffering is not working.

C#:
using System;
using System.Drawing;
using System.Windows.Forms;

namespace CheckerBoard
{
    public class MainForm : Form
    {
        const int CheckCount = 16;

        readonly PictureBox _pictureBox;
        bool _dark = false;

        readonly SolidBrush _whiteBrush = new SolidBrush(Color.White);
        readonly SolidBrush _blackBrush = new SolidBrush(Color.Black);
        readonly Pen _blackPen = new Pen(new SolidBrush(Color.Black));

        public MainForm()
        {
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(800, 600);
            Text = "Main Form";
            DoubleBuffered = true;

            _pictureBox = new PictureBox()
            {
                AutoSize = true,
                Dock = DockStyle.Fill,
            };
            _pictureBox.Paint += (o, e) => DrawBoard(e.Graphics, CheckCount, _dark);

            var timer = new Timer()
            {
                Interval = 250
            };
            timer.Tick += (o, e) => { _dark = !_dark; _pictureBox.Invalidate(); };

            Controls.Add(_pictureBox);
            CenterToScreen();

            Shown += (o, e) => timer.Start();
        }

        void DrawBoard(Graphics graphics, int checkCount, bool dark)
        {
            int boxWidth = Math.Min(_pictureBox.Width, _pictureBox.Height);
            int checkWidth = boxWidth / checkCount;
            int actualWidth = checkWidth * checkCount;
            int left = (_pictureBox.Width - actualWidth) / 2;
            int y = (_pictureBox.Height - actualWidth) / 2;
            var brush = !dark ? _blackBrush : _whiteBrush;

            graphics.FillRectangle(dark ? _blackBrush : _whiteBrush, _pictureBox.Bounds);
            graphics.DrawRectangle(_blackPen, left, y, actualWidth, actualWidth);

            for(int row = 0; row < checkCount; row++)
            {
                int start = (row % 2);
                int x = left + start * checkWidth;
                for (int col = start; col < checkCount; col += 2)
                {
                    graphics.FillRectangle(brush, x, y, checkWidth, checkWidth);
                    x += 2 * checkWidth;
                }
                y += checkWidth;
            }
        }

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}
 
Back
Top Bottom