预览形状WinForms时防止闪烁

时间:2019-01-06 16:20:40

标签: c# winforms

我试图在绘制形状之前预览和调整形状(就像在Paint中一样)。我遇到的问题是,即使我正在使用双缓冲,也仍然会出现可怕的闪烁,这是对包含临时元素的区域调用Invalidate()的逻辑结果。

另一个问题是使用此方法预览线会导致图形残留物堆积。

当我停止移动鼠标时,鼠标左键仍然按下时,形状也会消失。

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;
        }
    }
}

我可能会费劲去寻找解决方法。

Preview of the application

0 个答案:

没有答案