C# 使用撤消和重做功能绘制形状和字符串

C# 使用撤消和重做功能绘制形状和字符串,c#,.net,winforms,drawing,gdi+,C#,.net,Winforms,Drawing,Gdi+,有没有办法先抽绳,然后把它取下来 我使用了以下类来撤消/重做矩形、圆形、直线、箭头类型的形状,但我不知道如何删除绘制的字符串 下面是我在形状列表中添加矩形的方法:当我从列表中撤消或重做时,这很有效 抽绳 Shape shape = new Shape(); shape.shape = ShapesTypes.ShapeTypes.Rectangle; shape.CopyTuplePoints(points); shape.X = StartPoint.X; shape.Y = Start

有没有办法先抽绳,然后把它取下来

我使用了以下类来撤消/重做矩形、圆形、直线、箭头类型的形状,但我不知道如何删除绘制的字符串

下面是我在形状列表中添加矩形的方法:当我从列表中撤消或重做时,这很有效

抽绳

Shape shape = new Shape();
shape.shape = ShapesTypes.ShapeTypes.Rectangle;
shape.CopyTuplePoints(points);
shape.X = StartPoint.X;
shape.Y = StartPoint.Y;
shape.Width = EndPoint.X;
shape.Height = EndPoint.Y;

Pen pen = new Pen(new SolidBrush(penColor), 2);
shape.pen = pen;
undoactions.AddShape(shape);
以下是我绘制文本的方式:

var fontFamily = new FontFamily("Calibri");
var font = new Font(fontFamily, 12, FontStyle.Regular, GraphicsUnit.Point);

Size proposedSize = new Size(int.MaxValue, int.MaxValue);
TextFormatFlags flags = TextFormatFlags.WordEllipsis | TextFormatFlags.NoPadding | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.WordBreak;

Size size = TextRenderer.MeasureText(e.Graphics, textAreaValue, font, proposedSize, flags);

Shape shape = new Shape();
shape.shape = ShapesTypes.ShapeTypes.Text;
shape.X = ta.Location.X;
shape.Y = ta.Location.Y;
shape.Width = size.Width;
shape.Height = size.Height;
shape.Value = textAreaValue;

Pen pen = new Pen(new SolidBrush(penColor), 2);
shape.pen = pen;
undoactions.AddShape(shape);
但这不适用于撤消重做列表。也许问题出在钢笔和字体大小上,但我想不出如何使用带抽绳的钢笔

编辑: 以下是我在绘画比赛中的表现

protected override void OnPaint(PaintEventArgs e)
{
    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

    foreach (var item in undoactions.lstShape)
    {
        if (item.shape == ShapesTypes.ShapeTypes.Line)
        {
            e.Graphics.DrawLine(item.pen, item.X, item.Y, item.Width, item.Height);
        }
        else if (item.shape == ShapesTypes.ShapeTypes.Pen)
        {
            if (item.Points.Count > 1)
            {
                e.Graphics.DrawCurve(item.pen, item.Points.ToArray());
            }
        }

        else if (item.shape == ShapesTypes.ShapeTypes.Text)
        {
            var fontFamily = new FontFamily("Calibri");
            var font = new Font(fontFamily, 12, FontStyle.Regular, GraphicsUnit.Point);

            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
            e.Graphics.DrawString(item.Value, font, new SolidBrush(item.pen.Color), new PointF(item.X, item.Y));
        }
    }
}
Shape.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Drawing
{
    public class Shape : ICloneable
    {
        public ShapesTypes.ShapeTypes shape { get; set; }
        public List<Point> Points { get; }
        public int X { get; set; }
        public int Y { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public Pen pen { get; set; }

        public String Value { get; set; }

        public Shape()
        {
            Points = new List<Point>();
        }

        public void CopyPoints(List<Point> points)
        {
            for (int i = 0; i < points.Count; i++)
            {
                Point p = new Point();
                p.X = points[i].X;
                p.Y = points[i].Y;

                Points.Add(p);
            }
        }

        public void CopyCopyPoints(List<List<Point>> points)
        {
            for (int j = 0; j < points.Count; j++)
            {
                List<Point> current = points[j];

                for (int i = 0; i < current.Count; i++)
                {
                    Point p = new Point();
                    p.X = current[i].X;
                    p.Y = current[i].Y;

                    Points.Add(p);
                }
            }
        }

        public void CopyTuplePoints(List<Tuple<Point, Point>> points)
        {
            foreach (var line in points)
            {
                Point p = new Point();
                p.X = line.Item1.X;
                p.Y = line.Item1.Y;
                Points.Add(p);

                p.X = line.Item2.X;
                p.Y = line.Item2.Y;
                Points.Add(p);
            }
        }


        public object Clone()
        {
            Shape shp = new Shape();
            shp.X = X;
            shp.Y = Y;
            shp.Width = Width;
            shp.Height = Height;
            shp.pen = pen;
            shp.shape = shape;
            shp.Value = Value;

            for (int i = 0; i < Points.Count; i++)
            {
                shp.Points.Add(new Point(Points[i].X, Points[i].Y));
            }

            return shp;
        }
    }
}
撤消

if (currentshape != ShapesTypes.ShapeTypes.Undo)
{
    oldshape = currentshape;
    currentshape = ShapesTypes.ShapeTypes.Undo;
}
if (undoactions.lstShape.Count > 0)
{
    undoactions.Undo();
    this.Invalidate();
}
if (undoactions.redoShape.Count > 0)
{
    btnRedo.Enabled = true;
}
public class UndoRedo
{
    public List<Shape> lstShape = new List<Shape>();
    public List<Shape> redoShape = new List<Shape>();

    public void AddShape(Shape shape)
    {
        lstShape.Add(shape);
    }

    public void Undo()
    {
        redoShape.Add((Shape)lstShape[lstShape.Count - 1].Clone());
        lstShape.RemoveAt(lstShape.Count - 1);
    }

    public void Redo()
    {
        lstShape.Add((Shape)redoShape[redoShape.Count - 1].Clone());
        redoShape.RemoveAt(redoShape.Count - 1);
    }
}
撤消重做

if (currentshape != ShapesTypes.ShapeTypes.Undo)
{
    oldshape = currentshape;
    currentshape = ShapesTypes.ShapeTypes.Undo;
}
if (undoactions.lstShape.Count > 0)
{
    undoactions.Undo();
    this.Invalidate();
}
if (undoactions.redoShape.Count > 0)
{
    btnRedo.Enabled = true;
}
public class UndoRedo
{
    public List<Shape> lstShape = new List<Shape>();
    public List<Shape> redoShape = new List<Shape>();

    public void AddShape(Shape shape)
    {
        lstShape.Add(shape);
    }

    public void Undo()
    {
        redoShape.Add((Shape)lstShape[lstShape.Count - 1].Clone());
        lstShape.RemoveAt(lstShape.Count - 1);
    }

    public void Redo()
    {
        lstShape.Add((Shape)redoShape[redoShape.Count - 1].Clone());
        redoShape.RemoveAt(redoShape.Count - 1);
    }
}
公共类撤消重做
{
公共列表lstShape=新列表();
public List redoShape=new List();
公共空心形状(形状)
{
lstShape.Add(shape);
}
公共作废撤消()
{
redoShape.Add((Shape)lstShape[lstShape.Count-1].Clone());
lstShape.RemoveAt(lstShape.Count-1);
}
公共无效重做()
{
添加((Shape)redoShape[redoShape.Count-1].Clone());
redoShape.RemoveAt(redoShape.Count-1);
}
}

我做了一个类似的项目,在绘制形状并将其尺寸作为字符串写在图像上之后;按下Ctrl-Z/Ctrl-Y后,它执行撤消/重做对图像执行的操作

是指向我的Github项目的链接,这是一个C#win表单soln。运行soln后,工具使用说明将显示在工具本身上


希望这对您有所帮助……

今后,请遵循指导原则进行测试。这将帮助我们帮助你。例如,您可以排除所有与克隆相关的代码,因为它与您的问题无关

我对您的代码进行了一些重构,并创建了一个小的、可复制的示例。这个示例使用您概述的一般方法,因此我无法确切地告诉您为什么代码不起作用,除非您也可以发布一个类似的示例,我可以将其复制/粘贴到我的环境中。请不要链接到外部代码-它必须托管在这里

我对它进行了重构,以突出一些有助于使代码更易于维护的语言特性。请让我知道,如果你有任何问题,我把这里。请让我知道这是否有帮助。如果没有,请将其用作模板,并用您的代码替换我的代码,以便我可以帮助您

  public partial class Form1 : Form
  {
    private EntityBuffer _buffer = new EntityBuffer();
    private System.Windows.Forms.Button btnUndo;
    private System.Windows.Forms.Button btnRedo;

    public Form1()
    {
      this.btnUndo = new System.Windows.Forms.Button();
      this.btnRedo = new System.Windows.Forms.Button();
      this.SuspendLayout();

      this.btnUndo.Location = new System.Drawing.Point(563, 44);
      this.btnUndo.Name = "btnUndo";
      this.btnUndo.Size = new System.Drawing.Size(116, 29);
      this.btnUndo.TabIndex = 0;
      this.btnUndo.Text = "Undo";
      this.btnUndo.UseVisualStyleBackColor = true;
      this.btnUndo.Click += new System.EventHandler(this.btnUndo_Click);

      this.btnRedo.Location = new System.Drawing.Point(563, 79);
      this.btnRedo.Name = "btnRedo";
      this.btnRedo.Size = new System.Drawing.Size(116, 29);
      this.btnRedo.TabIndex = 0;
      this.btnRedo.Text = "Redo";
      this.btnRedo.UseVisualStyleBackColor = true;
      this.btnRedo.Click += new System.EventHandler(this.btnRedo_Click);

      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(800, 450);
      this.Controls.Add(this.btnRedo);
      this.Controls.Add(this.btnUndo);
      this.Name = "Form1";
      this.Text = "Form1";
      this.ResumeLayout(false);
    }

    protected override void OnLoad(EventArgs e)
    {
      _buffer.Add(new Rectangle(10, 10, 10, 10, Color.Red));
      _buffer.Add(new Rectangle(20, 20, 10, 10, Color.Red));
      _buffer.Add(new Rectangle(30, 30, 10, 10, Color.Red));
      _buffer.Add(new Text(40, 40, "Test", Color.Black));
      _buffer.Add(new Rectangle(50, 50, 10, 10, Color.Red));
      _buffer.Add(new Text(60, 60, "Test", Color.Black));
      base.OnLoad(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      foreach (var entity in _buffer.Entities)
        entity.Draw(e.Graphics);

      base.OnPaint(e);
    }

    private void btnUndo_Click(object sender, EventArgs e)
    {
      if (!_buffer.CanUndo)
        return;
      _buffer.Undo();
      Invalidate();
    }

    private void btnRedo_Click(object sender, EventArgs e)
    {
      if (!_buffer.CanRedo)
        return;
      _buffer.Redo();
      Invalidate();
    }
  }

  public abstract class Entity
  {
    public int X { get; set; }
    public int Y { get; set; }
    public Color Color { get; set; }

    public abstract void Draw(Graphics g);

    public Entity(int x, int y, Color color)
    {
      X = x;
      Y = y;
      Color = color;
    }
  }

  public class Text : Entity
  {
    private static Font _font = new Font(new FontFamily("Calibri"), 12, FontStyle.Regular, GraphicsUnit.Point);
    public string Value { get; set; }

    public Text(int x, int y, string value, Color color) : base(x,y,color) => Value = value;

    public override void Draw(Graphics g) => g.DrawString(Value, _font, new SolidBrush(Color), new PointF(X, Y));
  }

  public abstract class Shape : Entity
  {
    public int Width { get; set; }
    public int Height { get; set; }
    public Pen Pen { get; set; }

    public Shape(int x, int y, int width, int height, Color color) : base(x, y, color)
    {
      Width = width;
      Height = height;
    }
  }

  public class Rectangle : Shape
  {
    public Rectangle(Point start, Point end, Color color) : this(start.X, start.Y, end.X, end.Y, color) { }
    public Rectangle(int x, int y, int width, int height, Color color) : base(x, y, width, height, color) { }

    public override void Draw(Graphics g) => g.DrawRectangle(new Pen(new SolidBrush(Color)), X, Y, Width, Height);
  }

  public class EntityBuffer
  {
    public Stack<Entity> Entities { get; set; } = new Stack<Entity>();
    public Stack<Entity> RedoBuffer { get; set; } = new Stack<Entity>();
    public bool CanRedo => RedoBuffer.Count > 0;
    public bool CanUndo => Entities.Count > 0;

    public void Add(Entity entity)
    {
      Entities.Push(entity);
      RedoBuffer.Clear();
    }

    public void Undo() => RedoBuffer.Push(Entities.Pop());
    public void Redo() => Entities.Push(RedoBuffer.Pop());
  }
公共部分类表单1:表单
{
私有EntityBuffer _buffer=新EntityBuffer();
private System.Windows.Forms.Button btnUndo;
private System.Windows.Forms.Button btnRedo;
公共表格1()
{
this.btnUndo=new System.Windows.Forms.Button();
this.btnRedo=new System.Windows.Forms.Button();
这个.SuspendLayout();
this.btnUndo.Location=新系统.图纸.点(563,44);
this.btnUndo.Name=“btnUndo”;
this.btnUndo.Size=新系统.Drawing.Size(116,29);
this.btnUndo.TabIndex=0;
this.btnUndo.Text=“撤消”;
this.btnUndo.UseVisualStyleBackColor=true;
this.btnUndo.Click+=新建System.EventHandler(this.btnUndo\u Click);
this.btnRedo.Location=新系统.图纸.点(563,79);
this.btnRedo.Name=“btnRedo”;
this.btnRedo.Size=新系统.图纸.尺寸(116,29);
this.btnRedo.TabIndex=0;
this.btnRedo.Text=“重做”;
this.btnRedo.UseVisualStyleBackColor=true;
this.btnRedo.Click+=新建System.EventHandler(this.btnRedo\u Click);
此.AutoScaleDimensions=新系统.Drawing.SizeF(6F,13F);
this.AutoScaleMode=System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize=新系统图尺寸(800450);
this.Controls.Add(this.btnRedo);
this.Controls.Add(this.btnUndo);
this.Name=“Form1”;
this.Text=“Form1”;
此选项为.resume布局(false);
}
受保护的覆盖无效加载(事件参数e)
{
_添加(新矩形(10,10,10,10,Color.Red));
_添加(新的矩形(20,20,10,10,Color.Red));
_添加(新矩形(30,30,10,10,Color.Red));
_添加(新文本(40,40,“测试”,颜色为.Black));
_添加(新矩形(50,50,10,10,Color.Red));
_添加(新文本(60,60,“测试”,颜色.黑色));
基础荷载(e);
}
受保护的覆盖无效OnPaint(PaintEventArgs e)
{
foreach(缓冲区中的var实体)
实体绘制(如图形);
基础漆(e);
}
私有void btundo\u单击(对象发送方,事件参数e)
{
如果(!\u buffer.CanUndo)
返回;
_buffer.Undo();
使无效();
}
私有void btnRedo\u单击(对象发送方,事件参数e)
{
如果(!\u buffer.CanRedo)
返回;
_buffer.Redo();
使无效();
}
}
公共抽象类实体
{
公共整数X{get;set;}
公共整数Y{get;set;}
公共颜色{get;set;}
公开摘要作废图(图g);
公共实体(整数x、整数y、颜色)
{
X=X;
Y=Y;
颜色=颜色;
}
}
公共类文本:实体
{
私有静态字体_Font=新字体(新FontFamily(“Calibri”),12,FontStyle.Regular,GraphicsUnit.Point);
公共字符串值{get;set;}
公共文本(intx,inty,字符串值,颜色):基(x,y,颜色)=>value=value;
public override void Draw(图形g)=>g.DrawString(值,_字体,新的SolidBrush(颜色),新的PointF(X,Y));
}
公共抽象类形状:实体
{
公共整数宽度{get;set;}
公共整数高度{get;set;}
公共钢笔{get;set;}
公共形状(整数x,整数y,整数宽度,整数高度,颜色):基础(x,y,颜色)
{
宽度=宽度;
高度=高度;
}
}
公共类矩形:形状
{
公共矩形(点起点、点终点、颜色):此(起点.X,
public class TextShape : Shape {
    private string text;
    public string Text {
        get { return text; }
        set {
            if (text != value) {
                text = value;
                OnPropertyChanged();
            }
        }
    }

    private Point location;
    public Point Location {
        get { return location; }
        set {
            if (!location.Equals(value)) {
                location = value;
                OnPropertyChanged();
            }
        }
    }
    private Font font;
    public Font Font {
        get { return font; }
        set {
            if (font!=value) {
                font = value;
                OnPropertyChanged();
            }
        }
    }
    private Color color;
    public Color Color {
        get { return color; }
        set {
            if (color!=value) {
                color = value;
                OnPropertyChanged();
            }
        }
    }
    public override void Draw(Graphics g) {
        using (var brush = new SolidBrush(Color))
            g.DrawString(Text, Font, brush, Location);
    }

    public override Shape Clone() {
        return new TextShape() {
            Text = Text,
            Location = Location,
            Font = (Font)Font.Clone(),
            Color = Color
        };
    }
}
public class DrawingContext : INotifyPropertyChanged {
    public DrawingContext() {
        BackColor = Color.White;
        Shapes = new BindingList<Shape>();
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string name = "") {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    private Color backColor;
    public Color BackColor {
        get { return backColor; }
        set {
            if (!backColor.Equals(value)) {
                backColor = value;
                OnPropertyChanged();
            }
        }
    }
    private BindingList<Shape> shapes;
    public BindingList<Shape> Shapes {
        get { return shapes; }
        set {
            if (shapes != null)
                shapes.ListChanged -= Shapes_ListChanged;
            shapes = value;
            OnPropertyChanged();
            shapes.ListChanged += Shapes_ListChanged;
        }
    }
    private void Shapes_ListChanged(object sender, ListChangedEventArgs e) {
        OnPropertyChanged("Shapes");
    }
    public DrawingContext Clone() {
        return new DrawingContext() {
            BackColor = this.BackColor,
            Shapes = new BindingList<Shape>(this.Shapes.Select(x => x.Clone()).ToList())
        };
    }
}
public class DrawingSurface : Control {
    private Stack<DrawingContext> UndoBuffer = new Stack<DrawingContext>();
    private Stack<DrawingContext> RedoBuffer = new Stack<DrawingContext>();
    public DrawingSurface() {
        DoubleBuffered = true;
        CurrentDrawingContext = new DrawingContext();
        UndoBuffer.Push(currentDrawingContext.Clone());
    }
    DrawingContext currentDrawingContext;
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public DrawingContext CurrentDrawingContext {
        get {
            return currentDrawingContext;
        }
        set {
            if (currentDrawingContext != null)
                currentDrawingContext.PropertyChanged -= CurrentDrawingContext_PropertyChanged;
            currentDrawingContext = value;
            Invalidate();
            currentDrawingContext.PropertyChanged += CurrentDrawingContext_PropertyChanged;
        }
    }
    private void CurrentDrawingContext_PropertyChanged(object sender, PropertyChangedEventArgs e) {
        UndoBuffer.Push(CurrentDrawingContext.Clone());
        RedoBuffer.Clear();
        Invalidate();
    }

    public void Undo() {
        if (CanUndo) {
            RedoBuffer.Push(UndoBuffer.Pop());
            CurrentDrawingContext = UndoBuffer.Peek().Clone();
        }
    }
    public void Redo() {
        if (CanRedo) {
            CurrentDrawingContext = RedoBuffer.Pop();
            UndoBuffer.Push(CurrentDrawingContext.Clone());
        }
    }
    public bool CanUndo {
        get { return UndoBuffer.Count > 1; }
    }
    public bool CanRedo {
        get { return RedoBuffer.Count > 0; }
    }

    protected override void OnPaint(PaintEventArgs e) {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        using (var brush = new SolidBrush(CurrentDrawingContext.BackColor))
            e.Graphics.FillRectangle(brush, ClientRectangle);
        foreach (var shape in CurrentDrawingContext.Shapes)
            shape.Draw(e.Graphics);
    }
}