【问题标题】:How to use the Paint event to draw shapes at mouse coordinates如何使用 Paint 事件在鼠标坐标处绘制形状
【发布时间】:2019-05-11 10:02:25
【问题描述】:

显然,我最近开始在C# 编程,并试图做一个简单的WinForms 应用程序,它获取鼠标坐标并根据坐标缩放一个矩形。

我面临的问题是我不知道如何调用使用更多参数的方法(在本例中为xyPaintEventArgs)。或者,我确实知道如何处理 PaintEvent

这是整个代码,因为它非常简短且相当简单:

using System;
using System.Drawing;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        int x = e.X; 
        int y = e.Y;
        String data = (x.ToString() + " " + y.ToString());
        DrawRect(Something, x, y);
    }

    PaintEventArgs pEventArgs;
    private void Form1_Paint(object sender, PaintEventArgs e)
    {

    }

    public void DrawRect(PaintEventArgs e, int rey, int rex)
    {
        Graphics gr = e.Graphics;
        Pen pen = new Pen(Color.Azure, 4);
        Rectangle rect = new Rectangle(0, 0, rex, rey);
        gr.DrawRectangle(pen, rect);
    }
}

我正在尝试调用DrawRect()方法根据鼠标坐标用widthheight绘制矩形。

那么我怎样才能用坐标和PaintEventArgs 调用DrawRect()

【问题讨论】:

标签: c# .net winforms graphics paintevent


【解决方案1】:

在控件的表面上绘图时,您始终使用该控件的Paint 事件或覆盖自定义/用户控件的OnPaint 方法。
不要尝试存储其Graphics 对象:一旦控件失效(重新绘制),它就会变得无效。
使用PaintEventArgs对象提供的Graphics对象。

当需要更复杂的过程来绘制不同的形状时,您可以将e.Graphics 对象传递给不同的方法,这些方法将使用该对象来执行专门的绘图。


在示例中,每个绘制的形状的坐标和其他属性都分配给一个专门的类DrawingRectangle(这里是一个简化的结构,它可以包含更复杂的功能)。
List<DrawingRectangle>() 存储在 session 中生成的所有 DrawingRectangle 对象的引用(直到绘图被清除)。

每次在 Control 的表面上生成 Left MouseDown 事件时,都会将一个新的 DrawingRectangle 对象添加到列表中。
e.Location 存储为DrawingRectangle.StartPoint(一个不会改变的值)和DrawingRectangle.Location:当鼠标指针移动时,这个值将被更新。

当鼠标移动时,当前的e.Location 值会从之前存储的起点坐标中减去。一个简单的计算就可以从各个方向绘制形状。
此度量确定矩形的当前大小。

要从绘图中删除一个矩形,您只需从列表中删除它的引用和Invalidate()提供绘图表面的控件。
要清除绘图表面,请清除List<DrawingRectangle>() (drawingRects.Clear()) 并调用Invalidate()

这里还有一些其他示例:
Transparent Overlapping Circular Progress Bars
GraphicsPath and Matrix classes
Connecting different shapes
Drawing Transparent/Translucent Custom Controls

// Assign the Color used to draw the border of a shape to this Field
Color SelectedColor = Color.LightGreen;
List<DrawingRectangle> drawingRects = new List<DrawingRectangle>();

public class DrawingRectangle
{
    public Rectangle Rect => new Rectangle(Location, Size);
    public Size Size { get; set; }
    public Point Location { get; set; }
    public Control Owner { get; set; }
    public Point StartPosition { get; set; }
    public Color DrawingcColor { get; set; } = Color.LightGreen;
    public float PenSize { get; set; } = 3f;
}

private void form1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button != MouseButtons.Left) return; 
    DrawingRects.Add(new DrawingRectangle() {
        Location = e.Location,
        Size = Size.Empty,
        StartPosition = e.Location,
        Owner = (Control)sender,
        DrawingcColor = SelectedColor // <= Shape's Border Color
    });
}

private void form1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button != MouseButtons.Left) return;
    var dr = DrawingRects[DrawingRects.Count - 1];
    if (e.Y < dr.StartPosition.Y) { dr.Location = new Point(dr.Rect.Location.X, e.Y); }
    if (e.X < dr.StartPosition.X) { dr.Location = new Point(e.X, dr.Rect.Location.Y); }

    dr.Size = new Size(Math.Abs(dr.StartPosition.X - e.X), Math.Abs(dr.StartPosition.Y - e.Y));
    this.Invalidate();
}

private void form1_MouseUp(object sender, MouseEventArgs e)
{
    // The last drawn shape
    var dr = DrawingRects.Last();
    // ListBox used to present the shape coordinates
    lstPoints.Items.Add($"{dr.Location}, {dr.Size}");
}

private void form1_Paint(object sender, PaintEventArgs e)
{
    DrawShapes(e.Graphics);
}

private void DrawShapes(Graphics g)
{
    if (DrawingRects.Count == 0) return;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    foreach (var dr in DrawingRects) {
        using (Pen pen = new Pen(dr.DrawingcColor, dr.PenSize)) {
            g.DrawRectangle(pen, dr.Rect);
        };
    }
}

// A Button used to save the current drawing to a Bitmap
private void btnSave_Click(object sender, EventArgs e)
{
    using (var bitmap = new Bitmap(panCanvas.ClientRectangle.Width, panCanvas.ClientRectangle.Height))
    using (var g = Graphics.FromImage(bitmap)) {
        DrawShapes(g);
        bitmap.Save(@"[Image Path]", ImageFormat.Png);
        // Clone the Bitmap to show a thumbnail
    }
}

// A Button used to clear the current drawing
private void btnClear_Click(object sender, EventArgs e)
{
    drawingRects.Clear();
    this.Invalidate();
}

【讨论】:

  • 添加新矩形和绘制矩形是两个不同的步骤。您不应该在绘制事件中添加新矩形。
  • @jdweng 刚刚注意到这条评论。您可能指的是其他答案。这里,DrawingRectangle 是一个类对象。如果你更喜欢这个,你可以使用e.Graphics.DrawRectangle(Pen pen, float x, float y, float width, float height) 的形式。它不会改变任何东西。
【解决方案2】:

获取鼠标坐标并根据坐标缩放矩形的应用

我希望看到这样的东西(伪代码):

Point _point;

void Form1_MouseMove(object sender, MouseEventArgs e)
{
    ... // calculate new coordinates/scale factor/whatever here
    _point = ... ; // store results in fields
    Invalidate(); // this will cause repaint every time you move mouse
}

void Form1_Paint(object sender, PaintEventArgs e)
{
    ... // take values from fields
    e.Graphics.DrawRectangle(pen, rect); // draw
}

这很简单。绘画是Invalidate() 调用的组合,它引发了绘画事件。您使用字段传递的变量。

【讨论】:

    【解决方案3】:

    PaintEventArgs 允许您访问Graphics 对象,您需要那个对象来绘制东西。

    如果你不想使用PaintEventArgs,我建议你调用FormCreateGraphics()方法,它会允许你绘制矩形。

    为了提高性能,我建议您使用 using(...){ } 键来处理 Graphics 对象和 Pen 对象。

    您需要包含System.Drawing 才能使用GraphicsPen

    您的代码将如下所示:

    using System.Drawing;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace WindowsFormsApp2
    {
        public partial class Form1 : Form
        {
            Point _coordinates;
    
            public Form1()
            {
                this._coordinates = new Point();
                this.InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
            }
    
            public void Form1_MouseMove(object sender, MouseEventArgs e)
            {
                this._coordinates = new Point(e.X, e.Y);
                this.Invalidate();
            }
    
            private void Form1_Paint(object sender, PaintEventArgs e)
            {
                // Don't draw on first Paint event
                if(this._coordinates.X != 0 && this._coordinates.Y != 0)
                {
                    this.DrawRect(e);
                }
            }
    
            public void DrawRect(PaintEventArgs e)
            {
                using (Pen pen = new Pen(Color.Azure, 4))
                {
                    Rectangle rect = new Rectangle(0, 0, this._coordinates.X, this._coordinates.Y);
                    e.Graphics.DrawRectangle(pen, rect);
                }
            }
        }
    }
    

    【讨论】:

    • 这是错误的,您绘制新矩形而不擦除现有矩形,您最终会绘制许多矩形。
    • @kennyzx 你说得对,我会编辑我的答案,我没想到...对不起
    • 谢谢,它工作得很好,但我不明白一些事情。请向我解释一下好吗?坐标前的“_”指的是什么?为什么我需要如此频繁地使用“this”?谢谢
    • 你好@kuskus!首先,如果我的答案对您有用,您可以将其标记为“已接受的答案”吗? “_”用于告诉开发人员这是一个私有字段,“this”也是如此,它告诉开发人员“this”后面的成员。属于当前类。据我所知,它不会改变您的代码运行方式。祝你有个美好的一天!
    【解决方案4】:

    在 WinForms 应用程序中绘图的工作方式与您可能预期的略有不同。现在屏幕上的所有内容都被认为是暂时的,例如最小化并恢复您的窗口,屏幕上的内容将被删除,您将被要求重新绘制它(您的窗口的 Paint 事件将被系统触发)。

    这就是 DrawRect 方法需要 PaintEventArgs 参数的原因:它应该只在您的 Paint 事件处理程序中调用。如果您从外部调用它(就像其他答案中建议的那样),您的矩形可能会表现得不一致。

    我建议在一些内部变量中记住你的矩形,然后在系统要求时重新绘制它们:

    private Point pointToDrawRect = new Point(0,0);
    public void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            int x = e.X;
            int y = e.Y;
            String data = (x.ToString() + " " + y.ToString());
            pointToDrawRect= new Point(x, y);
            Invalidate();
        }
    
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
             if(pointToDrawRect.X != 0 || pointToDrawRect.Y != 0)
             {
                 DrawRect(e, pointToDrawRect.X, pointToDrawRect.Y);
             }
        }
    
        public void DrawRect(PaintEventArgs e, int rey, int rex)
        {
                using (Pen pen = new Pen(Color.Azure, 4))
                {
                    Rectangle rect = new Rectangle(0, 0, rex, rey);
                    e.Graphics.DrawRectangle(pen, rect);
                }
        }
    

    【讨论】:

      猜你喜欢
      • 2016-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-20
      • 2014-07-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多