在绘图中需要帮助

时间:2012-02-11 16:29:06

标签: c#

以下是我的代码。我想画线,填充矩形等.....

问题在于,假设我画一条线但是当我尝试绘制另一条线时,第一条绘制线消失了。所以我想要帮助,我将能够在一个窗体上绘制多个形状,并且第一个绘制线不会消失。

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

2 个答案:

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

这里我们声明我们将在派生类中重写的抽象方法PaintUpdateShapeUpdateShape将调用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);
    }
}

请注意,我们在形状类中执行所有形状特定的操作,而不是在表单中执行它们。形式中唯一需要关注不同形状的地方是我们创造不同形状的地方。这是一种典型的面向对象方法。它更易于维护和扩展。您可以添加新形状,只需对表单本身进行最小的更改。