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

标签 c# .net winforms drawing gdi+

有没有办法把绳子拉起来然后去掉?

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

https://github.com/Muhammad-Khalifa/Free-Snipping-Tool/blob/master/Free%20Snipping%20Tool/Operations/UndoRedo.cs

https://github.com/Muhammad-Khalifa/Free-Snipping-Tool/blob/master/Free%20Snipping%20Tool/Operations/Shape.cs

https://github.com/Muhammad-Khalifa/Free-Snipping-Tool/blob/master/Free%20Snipping%20Tool/Operations/ShapesTypes.cs

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

拉绳

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

但这不适用于撤消重做列表。也许问题出在 penfont-size 但我无法弄清楚如何将 pen 与 DrawString 一起使用。

编辑: 这是我在绘画事件中的绘画方式

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.Circle)
{
    Shape shape = new Shape();
    shape.shape = ShapesTypes.ShapeTypes.Circle;
    shape.CopyTuplePoints(cLines);
    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);
}

撤消

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

最佳答案

您可以创建一个 TextShape 派生自 Shape,具有 TextFontLocationColor 属性并将其视为其他形状,因此重做和撤消不会成为问题。

以下是一些可以帮助您解决问题的提示:

  • 创建一个基 Shape 类或接口(interface),其中包含基本方法,例如 DrawCloneHitTest 等。
  • 所有形状,包括 TextShape 都应派生自 ShapeTextShape 也是一种形状,具有 TextFontLocationColor 属性.
  • Shape 的每个实现都有其基方法的实现。
  • 在所有形状中实现 INotifyPropertyChanged,然后您可以监听属性的变化,例如,在更改颜色、边框宽度等后向撤消缓冲区添加一些内容。
  • 实现IClonable 或基类Clone 方法。添加到撤消缓冲区时,所有形状都应该是可复制的。
  • 处理 GDI 对象,如 PenBrush。这不是可选的。
  • 不是将单个形状添加到撤消缓冲区,而是创建一个类似于绘图上下文的类,其中包含形状列表、绘图表面的背景颜色等。同样在此类中实现 INotifyPropertyChanged,然后通过形状或此类属性的每次更改,您可以添加此类的克隆以撤消缓冲区。

形状

下面是一个Shape类的例子:

public abstract class Shape : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string name = "") {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    public abstract void Draw(Graphics g);
    public abstract Shape Clone();
}

文字形状

注意属性的实现以引发PropertyChanged 事件以及Clone 方法来克隆撤消缓冲区的对象,以及GDI 对象的使用方式绘制:

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

关于c# - 使用撤消和重做功能绘制形状和字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50690023/

相关文章:

c# - 调整矩形角

c# - 作为依赖项和不同的 publicKeyTokens 共享时的 RestSharp 错误

c# - 在 WinForm 应用程序中创建光滑的圆角

c# - 设置工作线程以持续检查对象的 bool 值

c# - 使用 LINQPad 查询包含嵌套元素的 XML 文件?

c# - IQueryable,将匿名类型转换为强类型

c# - 为接口(interface)重载 operator==

c# - 使用 C# 在 Azure 中创建资源组

.net - 正则表达式匹配区分大小写

c# - 重叠的半透明矩形