代码之家  ›  专栏  ›  技术社区  ›  m.qayyum

graphics.drawstring绘图后如何删除(撤消重做)?

  •  5
  • m.qayyum  · 技术社区  · 7 年前

    有没有办法把绳子拉下来然后取下来?

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

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

    但这不适用于撤消重做列表。也许问题出在 字体大小 但我想不出怎么用钢笔和拉绳。

    编辑: 以下是我在绘画活动中的表现

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

    外形尺寸

    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);
        }
    }
    
    3 回复  |  直到 7 年前
        1
  •  1
  •   Reza Aghaei    7 年前

    您可以创建 TextShape 源自 Shape ,拥有 Text , Font , Location Color 属性,并将其视为其他形状,因此“重做”和“撤消”不会成为问题。

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

    • 创建基础 形状 包含基本方法的类或接口,如 Draw , Clone , HitTest 等。
    • 所有形状,包括 文本形状 应该从 形状 . 文本形状 也是一个形状,有 文本 , 字体 , 位置 颜色 性能。
    • 每次执行 形状 有其基本方法的实现。
    • 实施 INotifyPropertyChanged 在所有形状中,然后可以侦听属性的更改,例如,在更改颜色、边框宽度等之后添加一些内容以撤消缓冲区。
    • 实施 IClonable 或基类 克隆 方法。添加到撤消缓冲区时,所有形状都应可克隆。
    • 像这样处理gdi对象 Pen Brush . 这不是可选的。
    • 不要添加单个形状来撤消缓冲区,而是创建一个类似的绘图上下文,其中包含形状列表、绘图表面的背景色等。同样在这个类中实现 更改了InotifyProperty ,然后根据形状或此类属性中的每次更改,可以添加此类的克隆以撤消缓冲区。

    形状

    下面是一个例子 形状 班级:

    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 事件和 克隆 方法来克隆用于撤消缓冲区的对象,以及在 :

    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);
        }
    }
    
        2
  •  2
  •   Robear    7 年前

    今后,请遵循 Minimal, Complete, and Verifiable example . 这将帮助我们帮助你。例如,您可以排除所有与克隆相关的代码,因为它与您的问题无关。

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

    我对它进行了重构,以突出一些有助于使代码更易于维护的语言特性。如果你对我放在这里的东西有任何疑问,请告诉我。如果有帮助,请告诉我。如果没有,请使用它作为模板,并用您的代码替换我的代码,以便我可以帮助您。

      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());
      }
    
        3
  •  0
  •   Sachin Patel    7 年前

    我做过一个类似的项目,在画出形状并把它的尺寸写在图像上,然后按 CTRL-Z/CTRL-Y键 它确实 撤销/重做 对图像执行的操作。

    Here 是指向github项目的链接,一个c win form soln。运行soln之后,工具使用说明将显示在工具本身上。

    希望这能帮助你…