2012-02-11 98 views
2

以下是我的代码。我试图绘制线,填充矩形等.....需要绘图帮助

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

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

让我把笔拿出来! – BrokenGlass 2012-02-11 16:30:40

+0

我建议你为每个控件使用'.Tag'属性,并在事件处理程序中使用'sender'对象来确定谁发送了消息。 – ja72 2012-02-11 18:24:33

回答

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

然后,你可以将所有的对象存储在列表中。列表中项目的顺序是您的z顺序,因此您可以将项目添加到列表中,然后通过Paint事件中的列表枚举并根据类型对每个项目进行不同的绘制。

从这里您可以存储笔和笔刷信息的类和其他信息。你的Paint事件可以告诉每个类自己绘画,它不需要知道它们是哪一种类型。

1

为了在Paint事件中绘制所有形状,需要存储所有形状的可能性,因为在每次调用Paint之前表单的背景都会重新绘制。让我们定义一个基类,从中我们会得到所有的形状

abstract class Shape 
{ 
    public abstract void Paint(PaintEventArgs e); 
    public abstract void UpdateShape(Point newLocation); 
} 

这里我们声明的抽象方法PaintUpdateShape,我们将在派生类重写。 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(); 
} 

添加到列表中鼠标移动,我们更新这样

最后的形状
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); 
    } 
} 

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

+0

我在自由曲线中添加了一些逻辑来平滑线条。现在可以将矩形和椭圆绘制到起点的左侧和顶部。代码经过测试并像奇迹一样运行。 – 2012-02-11 21:28:09