Arxeiss
Arxeiss

Reputation: 1066

C# Drawing - best solution

Today I am trying to solve problem with a blinking panel, when I draw onto it.

Lots of threads I read, like these:

So I tried to draw onto PictureBox, MyPanel with doubleBuffered, but the best solution I found, when I read, that I can't use g.Clear() every time, after that, even on non-doubleBuffered panel, blinking disappeared.

I even read, that I should free Graphics after draw is done. So I use everywhere using(Graphics g = panel.CreateGraphics()).

So my question, is it a great idea to create graphics for bitmap only when I draw something to it? Because before I created Bitmap, and Graphics (only for this bitmap, not for all components), so I had Graphics available for this bitmap every time

Here is my code:

public void newSizeDrawing()
    {
        Size size = collector.getLetterSize(selectedName);
        Size drawingSize = new Size(size.Width * (pixelSizeArray[pixelSize] + 1),size.Height * (pixelSizeArray[pixelSize] + 1));
        bitmapDraw = new Bitmap(drawingSize.Width, drawingSize.Height);
        int width = (this.MinimumSize.Width - panelDraw.MinimumSize.Width) + drawingSize.Width + 10;
        int height = (this.MinimumSize.Height - panelDraw.MinimumSize.Height) + drawingSize.Height + 10;
        this.Size = new Size(
            (width > this.MinimumSize.Width) ? width : this.MinimumSize.Width,
            (height > this.MinimumSize.Height) ? height : this.MinimumSize.Height);
        zeroDrawPosition = new Point((panelDraw.Size.Width - bitmapDraw.Width) / 2 - 1, (panelDraw.Size.Height - bitmapDraw.Height) / 2 - 1);
        using (Graphics g = panelDraw.CreateGraphics())
        {
            g.Clear(panelDraw.BackColor);
        }
        redrawDrawingLetter();
    }
public void redrawDrawingLetter()
    {
        bool[][] grid = collector.getArray(selectedName);
        using (Graphics graphicDraw = Graphics.FromImage(bitmapDraw))
        {
            graphicDraw.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
            graphicDraw.Clear(panelDraw.BackColor);
            int pxSize = pixelSizeArray[pixelSize];
            for (int y = 0; y < grid.Length; y++)
            {
                for (int x = 0; x < grid[y].Length; x++)
                {
                    graphicDraw.FillRectangle((grid[y][x] ? Brushes.Black : Brushes.White), x * (pxSize + 1), y * (pxSize + 1), pxSize, pxSize);
                }
            }
        }
        redrawDrawingPanel();
    }
private void redrawDrawingPanel()
    {
        using (Graphics g = panelDraw.CreateGraphics())
        {
            if (bitmapDraw != null)
                g.DrawImage(bitmapDraw, zeroDrawPosition);
        }
    }
private void panelDraw_Paint(object sender, PaintEventArgs e)
    {
        redrawDrawingPanel();
    }

Nobody can explain to me how to draw in C# the best way. So maybe my code isn't good, but that is reason why I asking how to do it correctly.

newSizeDrawing is called by myself only, when user click on + or - button. I have bool double-dimension array if pixel is on or off. This is program for drawing letters for microchips and LED display (often 8px height of letter).

I wrote a method that checks if the mouse moved from one "pixel" to another, so I don't redraw it after every call mouseMove event, because "pixel" can be from 10x10 px to 30x30 px.

Upvotes: 2

Views: 2358

Answers (1)

Hans Passant
Hans Passant

Reputation: 942358

private void panelDraw_Paint(object sender, PaintEventArgs e)
{
    redrawDrawingPanel();
}

This is fundamentally wrong. The Paint event passes e.Graphics to let you draw whatever you want to paint. When you turn on double-buffering, e.Graphics refers to a bitmap, it is initialized with the BackColor. You then proceed to drawing using another Graphics object you got from CreateGraphics(). That one draws directly to the screen.

The flicker effect you see if very pronounced. For a split second you see what the other Graphics context draws. Then your panelDraw_Paint() method returns and Winforms draws the double-buffered bitmap. There's nothing on it so it immediately erases what you drew.

Modify the redrawDrawingPanel() method and give it an argument of type Graphics. Pass e.Graphics in the call. And only use that Graphics object, remove all calls to CreateGraphics().

Upvotes: 1

Related Questions