Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
335 views
in Technique[技术] by (71.8m points)

c# - Draw multiple freehand Polyline or Curve drawing - Adding Undo Feature

I'm trying to create a simple drawing application with undo and redo features. I assume you can add what you are drawing into a list and calling upon the list to draw everything. Then undoing should just remove the last added item and redraw everything again. The problem is, how do I add what I've drawn into a list and use that list to undo?

I'm using the bitmap redraw method. This is how I draw:

    Point start, end;
    bool painting;
    private List<PointF> myPoints = new List<PointF>();

    private void pnlMain_MouseDown(object sender, MouseEventArgs e)
    {
        start = e.Location;
        painting = true;
    }

    private void pnlMain_MouseUp(object sender, MouseEventArgs e)
    {
        painting = false;
    }

    private void pnlMain_MouseMove(object sender, MouseEventArgs e)
    {
        if (painting == true)
        {
            end = e.Location;
            g.DrawLine(p, start, end);
            myPoints.Add(e.Location);
            pnlMain.Refresh();
            start = end;
        }
    }

    private void btnUndo_Click(object sender, EventArgs e)
    {
        g.Clear(cldFill.Color);
        if (myPoints.Count > 2)
        {
            myPoints.RemoveAt(myPoints.Count - 1);
            g.DrawCurve(p, myPoints.ToArray());
        }
        pnlMain.Refresh();
        //This works but you have to spam it to get rid of
        //a line and does some weird connections.
    }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You need to store lines in a List<List<Point>>. Each element of the list contains points of a drawing which you draw using a down, move and up. The next line which you draw, will store in the next element of list. Each undo, will remove the last drawing.

Put an instance of this control on your form and it will handle the drawing for you. Also to perform undo, call its Undo method.

using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public class DrawingSurface : Control
{
    public DrawingSurface() { this.DoubleBuffered = true; }
    List<List<Point>> Lines = new List<List<Point>>();
    bool drawing = false;
    protected override void OnMouseDown(MouseEventArgs e) {
        Lines.Add(new List<Point>());
        Lines.Last().Add(e.Location);
        drawing = true;
        base.OnMouseDown(e);
    }
    protected override void OnMouseMove(MouseEventArgs e) {
        if (drawing) { Lines.Last().Add(e.Location); this.Invalidate(); }
        base.OnMouseMove(e);
    }
    protected override void OnMouseUp(MouseEventArgs e) {
        if (drawing) {
            this.drawing = false;
            Lines.Last().Add(e.Location);
            this.Invalidate();
        }
        base.OnMouseUp(e);
    }
    protected override void OnPaint(PaintEventArgs e) {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        foreach (var item in Lines)
            e.Graphics.DrawLines(Pens.Black, item.ToArray()); /*or DrawCurve*/
    }
    public void Undo() {
        if (Lines.Count > 0) { this.Lines.RemoveAt(Lines.Count - 1); this.Invalidate(); }
    }
}

Note

  • Using this logic, you can simply implement redo using an other List<List<Point>>. It's enough to copy the last item before undo to redo list using RedoBuffer.Add(Lines.Last());. Then for each redo command, it's enough to add the last item of redo buffer to Lines and remove it from redo buffer. You should also clear the redo buffer after each mouse down.
  • You can use either of DrawLines or DrawCurve based on your requirement. DrawLines draws a poly-line, while DrawCurve draws a more smooth curve.

  • I prefer to encapsulate Lines.Count > 0 in a property like bool CanUndo and make it accessible from outside of control.

  • It's just an example and you can simply extend the solution. For example, instead of List<List<Point>> you can create a Shape class containing List<Point>, LineWidth, LineColor, etc and perform task using List<Shape>.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...