绘制多条徒手折线或曲线图 - 添加撤消功能
Draw multiple freehand Polyline or Curve drawing - Adding Undo Feature
我正在尝试创建一个具有撤消和重做功能的简单绘图应用程序。我假设您可以将要绘制的内容添加到列表中,然后调用列表绘制所有内容。然后撤消应该只删除最后添加的项目并重新绘制所有内容。问题是,如何将我绘制的内容添加到列表中并使用该列表撤消?
我正在使用位图重绘方法。
我是这样画的:
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<List<Point>>
中。列表中的每个元素都包含您使用向下、移动和向上绘制的绘图点。您绘制的下一行将存储在列表的下一个元素中。每次撤消都会删除最后一张绘图。
将此控件的一个实例放在您的窗体上,它将为您处理绘图。同样要执行撤消,请调用其 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<List<Point>>
简单地实现重做。使用 RedoBuffer.Add(Lines.Last());
将撤消之前的最后一项复制到重做列表就足够了。然后对于每个重做命令,将重做缓冲区的最后一项添加到 Lines
并将其从重做缓冲区中删除就足够了。您还应该在每次按下鼠标后清除重做缓冲区。
您可以根据需要使用 DrawLines
或 DrawCurve
中的任何一个。 DrawLines
画一条折线,而DrawCurve
画一条更平滑的曲线。
我更喜欢将 Lines.Count > 0
封装在 属性 中,例如 bool CanUndo
并使其可以不受控制地访问。
这只是一个示例,您可以简单地扩展解决方案。例如,您可以创建包含 List<Point>
、LineWidth
、LineColor
等的 Shape
class,并使用 [= 执行任务,而不是 List<List<Point>>
28=].
我正在尝试创建一个具有撤消和重做功能的简单绘图应用程序。我假设您可以将要绘制的内容添加到列表中,然后调用列表绘制所有内容。然后撤消应该只删除最后添加的项目并重新绘制所有内容。问题是,如何将我绘制的内容添加到列表中并使用该列表撤消?
我正在使用位图重绘方法。 我是这样画的:
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<List<Point>>
中。列表中的每个元素都包含您使用向下、移动和向上绘制的绘图点。您绘制的下一行将存储在列表的下一个元素中。每次撤消都会删除最后一张绘图。
将此控件的一个实例放在您的窗体上,它将为您处理绘图。同样要执行撤消,请调用其 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<List<Point>>
简单地实现重做。使用RedoBuffer.Add(Lines.Last());
将撤消之前的最后一项复制到重做列表就足够了。然后对于每个重做命令,将重做缓冲区的最后一项添加到Lines
并将其从重做缓冲区中删除就足够了。您还应该在每次按下鼠标后清除重做缓冲区。 您可以根据需要使用
DrawLines
或DrawCurve
中的任何一个。DrawLines
画一条折线,而DrawCurve
画一条更平滑的曲线。我更喜欢将
Lines.Count > 0
封装在 属性 中,例如bool CanUndo
并使其可以不受控制地访问。这只是一个示例,您可以简单地扩展解决方案。例如,您可以创建包含
List<Point>
、LineWidth
、LineColor
等的Shape
class,并使用 [= 执行任务,而不是List<List<Point>>
28=].