0

I'm trying to preview and resize shapes (just like you would do in Paint) before drawing them. The problem I encountered is that even though I'm using double buffering, I still get horrible flickering, which is a logical result of calling Invalidate() for the region containing the temporary element.

Another issue is that previewing lines using this method causes graphical residue to build up.

The shape also disappears when I stop moving the mouse, with the left mouse button still held down.

PaintForm.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace MyPaint
{
    public partial class PaintForm : Form
    {
        // Where user clicks the first time.
        private Point start = Point.Empty;
        // Where user releases the mouse button.
        private Point end = Point.Empty;

        private DrawingElementType currentElementType = DrawingElementType.Line;

        private List<DrawingElement> drawingElements;

        // Used for double buffering (aka first drawing on a bitmap then drawing the bitmap onto the control).
        private Bitmap bmp;

        public PaintForm()
        {
            InitializeComponent();
            drawingElements = new List<DrawingElement>();
            bmp = new Bitmap(pbCanvas.Width, pbCanvas.Height, PixelFormat.Format24bppRgb);
            Graphics.FromImage(bmp).Clear(Color.White);
        }

        private void pbCanvas_Paint(object sender, PaintEventArgs e)
        {
            if (bmp != null)
            {
                bmp.Dispose();
                bmp = null;
            }

            bmp = new Bitmap(pbCanvas.Width, pbCanvas.Height, PixelFormat.Format24bppRgb);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.Clear(Color.White);
                g.SmoothingMode = SmoothingMode.AntiAlias;
                foreach (var elem in drawingElements)
                {
                    switch (elem.Type)
                    {
                        case DrawingElementType.Line:
                            g.DrawLine(new Pen(Color.BlueViolet), elem.Start, elem.End);
                            break;
                        case DrawingElementType.Rectangle:
                            g.FillRectangle(Brushes.Black, elem.Container);
                            break;
                        case DrawingElementType.Ellipse:
                            g.FillEllipse(Brushes.Bisque, elem.Container);
                            break;
                        default:
                            break;
                    }
                }
            }

            e.Graphics.DrawImageUnscaled(bmp, new Point(0, 0));
        }

        private void pbCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                start = e.Location;
            }
        }

        private void pbCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                var temp = new DrawingElement(currentElementType, start, e.Location);
                using (Graphics g = pbCanvas.CreateGraphics())
                {
                    switch (currentElementType)
                    {
                        case DrawingElementType.Line:
                            g.DrawLine(new Pen(Color.BlueViolet), temp.Start, temp.End);
                            break;
                        case DrawingElementType.Rectangle:
                            g.FillRectangle(Brushes.Black, temp.Container);
                            break;
                        case DrawingElementType.Ellipse:
                            g.FillEllipse(Brushes.Bisque, temp.Container);
                            break;
                        default:
                            break;
                    }
                }
                pbCanvas.Invalidate(temp.Container);
            }
        }

        private void pbCanvas_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                end = e.Location;

                var elem = new DrawingElement(currentElementType, start, end);
                drawingElements.Add(elem);

                // This triggers the paint event and only repaints the region of the new element.
                pbCanvas.Invalidate(elem.Container);
            }
        }

        private void btnLine_Click(object sender, EventArgs e)
        {
            currentElementType = DrawingElementType.Line;
        }

        private void btnEllipse_Click(object sender, EventArgs e)
        {
            currentElementType = DrawingElementType.Ellipse;
        }

        private void btnlRectangle_Click(object sender, EventArgs e)
        {
            currentElementType = DrawingElementType.Rectangle;
        }
    }
}

DrawingElement.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyPaint
{
    enum DrawingElementType
    {
        Line,
        Rectangle,
        Ellipse
    }

    class DrawingElement
    {
        public Point Start { get; set; }
        public Point End { get; set; }
        public DrawingElementType Type { get; }

        // Returns the region of the control that contains the element.
        public Rectangle Container
        {
            get
            {
                int width = Math.Abs(End.X - Start.X);
                int height = Math.Abs(End.Y - Start.Y);
                return new Rectangle(Math.Min(Start.X, End.X), Math.Min(Start.Y, End.Y), width, height);
            }
        }

        public DrawingElement(DrawingElementType type, Point start, Point end)
        {
            Type = type;
            Start = start;
            End = end;
        }
    }
}

I probably fried my brain trying to find workarounds.

Preview of the application

Hajduk
  • 1
  • 1
  • Perhaps duplicate of https://stackoverflow.com/questions/8046560/how-to-stop-flickering-c-sharp-winforms – chronoxor Jan 06 '19 at 16:24
  • `using (Graphics g = pbCanvas.CreateGraphics())` then `pbCanvas.Invalidate();`. You must have see, while seaching SO, people recommending (because they can't forbid) not to use `Control.CreateGraphics()` for painting. All the painting must be done in the Paint event of a Control. -- If you use a Control which has a Bitmap as background and you want to paint some shapes on it, this controls must have styles: `ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer` se to `true`. The accepted answer in the link aknowledges this. – Jimi Jan 06 '19 at 19:03

0 Answers0