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

问题描述 投票:5回答:3

有没有办法拉线然后将其除去?

我已经使用以下类来撤消/重做矩形,圆形,直线,箭头类型的形状,但无法确定如何删除绘制的字符串。

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

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

DrawString

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

但是这不适用于撤消重做列表。也许问题出在[[pen和font-size,但我无法弄清楚如何在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; } } }

DrawCircle

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

UndoRedo

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); } }
c# .net winforms drawing gdi+
3个回答
1
投票
您可以创建具有TextShapeShapeTextFont属性的,从Location派生的Color,并像对待其他形状一样对待它,因此重做和撤消将不是问题。 >

这里有一些技巧可以帮助您解决问题:

    创建包含基础方法(例如ShapeDrawClone等的基本HitTest类或接口
  • [包括TextShape的所有形状均应源自ShapeTextShape也是具有TextFontLocationColor属性的形状。
  • [C0的每个实现都有其基本方法的实现。
  • 在所有形状中添加Shape,然后您就可以收听属性的变化,例如,在颜色,边框宽度等发生变化后,将某些内容添加到撤消缓冲区中]
  • 实现INotifyPropertyChanged或基类IClonable方法。添加到撤消缓冲区时,所有形状都应该是可克隆的。
  • [请处置ClonePen之类的GDI对象。这不是可选的。
  • 而不是向撤消缓冲区添加单个形状,而是创建一个类似绘图上下文的类,其中包含形状列表,绘图表面的背景颜色等。同样,在此类实现Brush中,然后通过形状或此类属性的每次更改,可以将此类的副本添加到撤消缓冲区。
  • Shape

这里是INotifyPropertyChanged类的示例:

Shape

TextShape

请注意引发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对象的方式:

Draw

DrawingContext

实际上,此类包含所有形状和其他一些属性,例如绘图表面的底色。这是您需要将其克隆添加到撤消缓冲区的类:

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

DrawingSurface

此类实际上是具有撤消和重做功能并且还在其表面上绘制当前图形上下文的控件:

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); } } 的准则。这将帮助我们为您提供帮助。例如,您可能已排除了与克隆相关的所有代码,因为它与您的问题无关。

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

我对其进行了重构,以突出显示一些语言功能,这些功能可以帮助使您的代码更具可维护性。如果您对我在这里的内容有任何疑问,请告诉我。请让我知道这可不可以帮你。如果没有,请使用它作为模板,并用您的代码替换我的代码,以便我为您提供帮助。

Minimal, Complete, and Verifiable example

在绘制形状并将尺寸标注为图像上的字符串后,我做了类似的项目;按下

Ctrl-Z / Ctrl-Y

后,会进行撤消/重做
对图像执行的操作。 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()); } 是我的Github项目的链接,这是一个C#win-form解决方案。运行soln之后,工具使用说明将出现在工具本身上。

希望这对您有帮助...


2
投票
[将来,请遵循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); } } 的准则。这将帮助我们为您提供帮助。例如,您可能已排除了与克隆相关的所有代码,因为它与您的问题无关。

0
投票
在绘制形状并将尺寸标注为图像上的字符串后,我做了类似的项目;按下

Ctrl-Z / Ctrl-Y

© www.soinside.com 2019 - 2024. All rights reserved.