有没有办法把绳子拉起来然后去掉?
我使用了以下类来撤消/重做矩形、圆形、直线、箭头类型的形状,但不知道如何删除绘制的字符串。
这是我在形状列表中添加矩形的方法:当我从列表中撤消或重做时,这很有效。
拉绳
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 但我无法弄清楚如何将 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
,具有 Text
、Font
、Location
和 Color
属性并将其视为其他形状,因此重做和撤消不会成为问题。
以下是一些可以帮助您解决问题的提示:
- 创建一个基
Shape
类或接口(interface),其中包含基本方法,例如Draw
、Clone
、HitTest
等。 - 所有形状,包括
TextShape
都应派生自Shape
。TextShape
也是一种形状,具有Text
、Font
、Location
和Color
属性. Shape
的每个实现都有其基方法的实现。- 在所有形状中实现
INotifyPropertyChanged
,然后您可以监听属性的变化,例如,在更改颜色、边框宽度等后向撤消缓冲区添加一些内容。 - 实现
IClonable
或基类Clone
方法。添加到撤消缓冲区时,所有形状都应该是可复制的。 - 处理 GDI 对象,如
Pen
和Brush
。这不是可选的。 - 不是将单个形状添加到撤消缓冲区,而是创建一个类似于绘图上下文的类,其中包含形状列表、绘图表面的背景颜色等。同样在此类中实现
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/