以下是我的代码。我想画线,填充矩形等.....
问题在于,假设我画一条线但是当我尝试绘制另一条线时,第一条绘制线消失了。所以我想要帮助,我将能够在一个窗体上绘制多个形状,并且第一个绘制线不会消失。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace finalPaint
{
public partial class Form1 : Form
{
List<Point> points = new List<Point>();
Rectangle rect;
Point first;
Point last;
string op;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
points.Add(e.Location);
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
last = e.Location;
this.Invalidate();
this.Update();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
switch (op)
{
case "toolStripButton1":
{
if (points.Count > 2)
{
e.Graphics.DrawLines(Pens.Black, points.ToArray());
}
}
break;
case "toolStripButton2":
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, rect);
}
}
break;
case "toolStripButton3":
{
Pen pen = new Pen(Color.Red, 2);
e.Graphics.DrawLine(pen, first, last);
this.Update();
}
break;
case "toolStripButton4":
{
using (SolidBrush pen = new SolidBrush(Color.Red))
{
e.Graphics.FillRectangle(pen, rect);
}
}
break;
case "toolStripButton5":
{
using (SolidBrush pen = new SolidBrush(Color.Red))
{
e.Graphics.FillEllipse(pen, rect);
}
}
break;
case "toolStripButton6":
{
using (Pen pen = new Pen(Color.Red,2))
{
e.Graphics.DrawEllipse(pen, rect);
}
}
break;
default:
break;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
rect = new Rectangle(e.X, e.Y, 0, 0);
first = e.Location;
this.Invalidate();
}
private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
private void selectedButton(object sender, EventArgs e)
{
foreach (ToolStripButton btn in toolStrip1.Items)
{
btn.Checked = false;
}
ToolStripButton btnClicked = sender as ToolStripButton;
btnClicked.Checked = true;
op = btnClicked.Name;
}
}
}
答案 0 :(得分:6)
在每个Paint事件中,您需要在屏幕上绘制所需的所有对象。你不只是在已经存在的东西上绘画。你正在重画整个场景。
这样做的原因是您的控件可能会在某些时候被视图遮挡,而Windows会在再次显示时重新绘制它。
如果要保留所有对象的内存,则需要将它们存储在代码中。由于每个对象都不同(线条,矩形,椭圆),因此您需要以允许区分的方式存储它们。你可以创建这样的类:
public class DrawingShape
{
public string Name { get; set; }
public DrawingShapeType Type { get; set; }
// other shared properties
public virtual void Draw(Graphics g)
{
}
}
public class DrawingRectangle : DrawingShape
{
public DrawingRectangle()
{
Name = "Rectangle";
Type = DrawingShapeType.Rectangle;
}
public override void Draw(Graphics g)
{
// draw this shape
}
}
public enum DrawingShapeType
{
Rectangle,
Ellipse,
Line,
}
然后您可以将所有对象存储在List中。列表中项目的顺序是您的z顺序,因此您可以将项目添加到列表中,并通过Paint事件中的列表进行枚举,并根据类型以不同方式绘制每个项目。
从这里您可以在班级和其他信息中存储笔和画笔信息。您的Paint事件可以告诉每个类绘制自己,并且不需要知道它们是哪种类型。
答案 1 :(得分:1)
您需要存储所有形状,以便在Paint
事件中全部绘制它们,因为在每次调用Paint
之前,表单的背景都会重新绘制。让我们定义一个基类,我们将从中导出所有形状
abstract class Shape
{
public abstract void Paint(PaintEventArgs e);
public abstract void UpdateShape(Point newLocation);
}
这里我们声明我们将在派生类中重写的抽象方法Paint
和UpdateShape
。 UpdateShape
将调用MouseMove
。
让我们从自由形式的行开始
class FreeformLine : Shape
{
private List<Point> _points = new List<Point>();
public FreeformLine(Point startLocation)
{
_points.Add(startLocation);
}
public override void Paint(PaintEventArgs e)
{
if (_points.Count >= 2) {
e.Graphics.DrawLines(Pens.Black, _points.ToArray());
}
}
public override void UpdateShape(Point newLocation)
{
const int minDist = 3;
// Add new point only if it has a minimal distance from the last one.
// This creates a smoother line.
Point last = _points[_points.Count - 1];
if (Math.Abs(newLocation.X - last.X) >= minDist ||
Math.Abs(newLocation.Y - last.Y) >= minDist)
{
_points.Add(newLocation);
}
}
}
这里我们需要一个点列表。在构造函数中,我们传递第一个点。 Paint
方法只执行您已定义的绘制逻辑,UpdateShape
方法将新点添加到我们的点列表中。
直线的工作方式非常相似,但只定义了第一个和最后一个点。
class StraightLine : Shape
{
private Point _first;
private Point _last;
public StraightLine(Point startLocation)
{
_first = startLocation;
}
public override void Paint(PaintEventArgs e)
{
if (!_last.IsEmpty) {
Pen pen2 = new Pen(Color.Red, 2);
e.Graphics.DrawLine(pen2, _first, _last);
}
}
public override void UpdateShape(Point newLocation)
{
_last = newLocation;
}
}
我们只定义一个矩形类并添加一个变量,以便记住形状是否已填充。
class RectangleShape : Shape
{
protected bool _filled;
protected Rectangle _rect;
protected Point _start;
public RectangleShape(Point startLocation, bool filled)
{
_start = startLocation;
_rect = new Rectangle(startLocation.X, startLocation.Y, 0, 0);
_filled = filled;
}
public override void Paint(PaintEventArgs e)
{
if (_filled) {
using (SolidBrush brush = new SolidBrush(Color.Red)) {
e.Graphics.FillRectangle(brush, _rect);
}
} else {
using (Pen pen = new Pen(Color.Red, 2)) {
e.Graphics.DrawRectangle(pen, _rect);
}
}
}
public override void UpdateShape(Point newLocation)
{
int x = Math.Min(_start.X, newLocation.X);
int y = Math.Min(_start.Y, newLocation.Y);
int width = Math.Abs(newLocation.X - _start.X);
int height = Math.Abs(newLocation.Y - _start.Y);
_rect = new Rectangle(x, y, width, height);
}
}
最后,我们声明了椭圆类。由于这个也使用矩形,我们只是从矩形类派生它。
class Ellipse : RectangleShape
{
public Ellipse(Point startLocation, bool filled)
: base(startLocation, filled)
{
}
public override void Paint(PaintEventArgs e)
{
if (_filled) {
using (SolidBrush brush = new SolidBrush(Color.Red)) {
e.Graphics.FillEllipse(brush, _rect);
}
} else {
using (Pen pen = new Pen(Color.Red, 2)) {
e.Graphics.DrawEllipse(pen, _rect);
}
}
}
}
这里我们只覆盖Paint
方法。所有矩形更新逻辑都保持不变。
现在到表格。这里我们声明全局变量
List<Shape> _shapes = new List<Shape>();
Shape _lastShape;
string op;
在鼠标按下事件中,我们创建一个新形状并将其添加到列表中,如此
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
switch (op) {
case "toolStripButton1":
_lastShape = new FreeformLine(e.Location);
break;
case "toolStripButton2":
_lastShape = new RectangleShape(e.Location, false);
break;
case "toolStripButton3":
_lastShape = new StraightLine(e.Location);
break;
case "toolStripButton4":
_lastShape = new RectangleShape(e.Location, true);
break;
case "toolStripButton5":
_lastShape = new Ellipse(e.Location, true);
break;
case "toolStripButton6":
_lastShape = new Ellipse(e.Location, false);
break;
default:
break;
}
_shapes.Add(_lastShape);
Refresh();
}
在MouseMove中,我们更新最后一个形状
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _lastShape != null) {
_lastShape.UpdateShape(e.Location);
this.Refresh();
}
}
Paint
方法现在更加简单
private void Form1_Paint(object sender, PaintEventArgs e)
{
foreach (Shape shape in _shapes) {
shape.Paint(e);
}
}
请注意,我们在形状类中执行所有形状特定的操作,而不是在表单中执行它们。形式中唯一需要关注不同形状的地方是我们创造不同形状的地方。这是一种典型的面向对象方法。它更易于维护和扩展。您可以添加新形状,只需对表单本身进行最小的更改。