【问题标题】:Draw multiple freehand Polyline or Curve drawing - Adding Undo Feature绘制多条手绘折线或曲线图 - 添加撤消功能
【发布时间】:2016-11-12 19:08:46
【问题描述】:

我正在尝试创建一个具有撤消和重做功能的简单绘图应用程序。我假设您可以将您正在绘制的内容添加到列表中并调用列表来绘制所有内容。然后撤消应该只删除最后添加的项目并重新绘制所有内容。问题是,如何将我绘制的内容添加到列表中并使用该列表撤消?

我正在使用位图重绘方法。 我就是这样画的:

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

【问题讨论】:

  • 您需要收集您在List&lt;T&gt; 中绘制的坐标。有关如何收集它们的代码,请参阅我的comments here!另外:你画错了。您似乎缓存了一个 Graphics 对象。这是错误的!在 Paint 事件中绘制所有内容!
  • 是的,没问题。如果你实现了我在(all) my comments I linked to 中给出的代码,你只需要添加一个按钮点击curves.Remove(curves.Last)); pnlMain.Invalidate();

标签: c# winforms graphics gdi+


【解决方案1】:

您需要将行存储在List&lt;List&lt;Point&gt;&gt; 中。列表的每个元素都包含您使用向下、移动和向上绘制的绘图点。您绘制的下一行将存储在列表的下一个元素中。每次撤消都会删除最后一张图。

将此控件的实例放在您的表单上,它会为您处理绘图。同样要执行撤消,请调用其Undo 方法。

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

注意

  • 使用此逻辑,您可以使用另一个List&lt;List&lt;Point&gt;&gt; 简单地实现重做。使用RedoBuffer.Add(Lines.Last()); 将撤消前的最后一项复制到重做列表就足够了。然后对于每个重做命令,将重做缓冲区的最后一项添加到Lines 并从重做缓冲区中删除就足够了。您还应该在每次按下鼠标后清除重做缓冲区。
  • 您可以根据需要使用DrawLinesDrawCurveDrawLines 绘制折线,而DrawCurve 绘制更平滑的曲线。

  • 我更喜欢将Lines.Count &gt; 0 封装在bool CanUndo 之类的属性中,并使其不受控制地访问。

  • 这只是一个示例,您可以简单地扩展解决方案。例如,代替List&lt;List&lt;Point&gt;&gt;,您可以创建一个包含List&lt;Point&gt;LineWidthLineColor 等的Shape 类,并使用List&lt;Shape&gt; 执行任务。

【讨论】:

  • 这是一堂非常好的课!你能不能给我看一个例子,说明我如何与班级交谈并告诉它画矩形或圆形?
  • 为了扩展这个例子,你可以创建一个 Shape 类,其中包含一个抽象的 Draw 方法和一些派生类,如 RectangleCircle 实现该方法并包含坐标所需的属性。然后您可以使用List&lt;Shape&gt; 来存储您要绘制的对象。要绘制形状,您应该有一个标志,指示您正在绘制的形状并基于此标志,将所需的形状添加到该列表中。要绘制它们,在OnPaint 方法中,调用每个形状的Draw 方法。
  • 这几乎是整个想法。这很简单,但需要更多代码。它也可以通过不同的方式完成,所以如果您开始编写应用程序,请随时提出与您的实现相关的问题。希望这个答案和 cmets 对您有所帮助:)
  • 显而易见:这是一个无限撤消:-) - 见here for an example of saving the work
猜你喜欢
  • 2019-10-27
  • 1970-01-01
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多