Drawing a line by mouse in a panel

2019-09-02 03:08发布

问题:

I want to draw a line by mouse(interactively) , I used C# and WinForm, the line should appear at any time from the starting point(when the mouse press on the panel) to the current position of the mouse, exactly like drawing a line in Paint program.

but the code produces a lot of lines, i know why but i don't know how to overcome this problem

Here is my code:

namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    Graphics g;
    Pen myPen = new Pen(Color.Red);
    Point p = new Point();
    bool flag = false;


    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        flag = true;
        p = e.Location;
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if (flag)
        {
            g = panel1.CreateGraphics();
            myPen.Width = 3;

            Point p2 = new Point();
            p2 = e.Location;

            g.DrawLine(myPen, p, p2);

        }
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        flag = false;
    }

}}

Any Help? i want to draw many lines and keep the code simple as possible!

回答1:

You will need to better manage the drawing. Some pointers:

  1. Don't use CreateGraphics. Instead, use the Paint event already provided by the control.
  2. Do your drawing in an inherited class of your own. Don't draw in the Form class unless you're drawing on the form.

Here's an example class. It's inherited from Panel. Simply add this to a form, such as in the Form's constructor using something like this.Controls.Add(new PanelWithMouseDraw());.

Note: this uses Tuple which I believe requires .NET 4.0 or above. You could replace this structure with something else, if need be...you just need to keep a list of Point pairs.

namespace WindowsFormsApplication1
{
    public class PanelWithMouseDraw : Panel
    {
        private Point _origin = Point.Empty;
        private Point _terminus = Point.Empty;
        private Boolean _draw = false;
        private List<Tuple<Point, Point>> _lines = new List<Tuple<Point, Point>>();

        public PanelWithMouseDraw()
        {
            Dock = DockStyle.Fill;
            DoubleBuffered = true;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Button == MouseButtons.Left)
            {
                _draw = true;
                _origin = e.Location;
            }
            else
            {
                _draw = false;
                _origin = Point.Empty;
            }

            _terminus = Point.Empty;
            Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (_draw && !_origin.IsEmpty && !_terminus.IsEmpty)
                _lines.Add(new Tuple<Point, Point>(_origin, _terminus));
            _draw = false;
            _origin = Point.Empty;
            _terminus = Point.Empty;
            Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == MouseButtons.Left)
                _terminus = e.Location;
            Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            foreach (var line in _lines)
                e.Graphics.DrawLine(Pens.Blue, line.Item1, line.Item2);
            if (!_origin.IsEmpty && !_terminus.IsEmpty)
                e.Graphics.DrawLine(Pens.Red, _origin, _terminus);
        }
    }
}


回答2:

Simple fix, change the method panel1_MouseMove as follows:

private void panel1_MouseMove(object sender, MouseEventArgs e)
{
    if (flag)
    {
        g = panel1.CreateGraphics();
        myPen.Width = 3;

        Point p2 = new Point();
        p2 = e.Location;

        g.DrawLine(myPen, p, p2);
        p = p2; // just add this

    }
}

Keep in mind this will work with any mouse button down, left or right doesnt matter.

Edit1:

This should draw a straight line and all the previous ones.

namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
    public struct Line
    {
        public Point start;
        public Point end;
    }

    public Form1()
    {
        InitializeComponent();
    }

    Pen erasePen = new Pen(Color.White, 3.0F);
    Pen myPen = new Pen(Color.Red, 3.0F);
    Point p = new Point();
    Point endPoint = new Point();
    bool flag = false;
    List<WindowsFormsApplication2.Form1.Line> lines = new List<WindowsFormsApplication2.Form1.Line>();


    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        flag = true;
        p = e.Location;
        endPoint = p;
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if (flag)
        {
            Graphics g = panel1.CreateGraphics();

            Point p2 = e.Location;

            EraseLine(p, endPoint, g);

            DrawAllLines(lines, g);

            DrawLine(p, p2, g);

            endPoint = p2;

            g.Dispose();
        }
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        // redraw for one last time...
        Graphics g = panel1.CreateGraphics();

        Point p2 = e.Location;

        lines.Add(new Line { start = p, end = p2} );

        EraseLine(p, endPoint, g);

        DrawAllLines(lines, g);

        flag = false;

        g.Dispose();
    }

    private void DrawLine(Point start, Point end, Graphics g)
    {
        g.DrawLine(myPen, start, end);
    }

    private void DrawLine(WindowsFormsApplication2.Form1.Line line, Graphics g)
    {
        g.DrawLine(myPen, line.start, line.end);
    }

    private void DrawAllLines(List<WindowsFormsApplication2.Form1.Line> allLines, Graphics g)
    {
        foreach(WindowsFormsApplication2.Form1.Line l in allLines)
        {
            g.DrawLine(myPen, l.start, l.end);
        }
    }

    private void EraseLine(Point start, Point end, Graphics g)
    {
        g.DrawLine(erasePen, start, end);
    }

}}