如何使用 Paint 事件在鼠标坐标处绘制形状
How to use the Paint event to draw shapes at mouse coordinates
显然,我最近开始在 C#
编程,并且正在尝试做一个简单的 WinForms
应用程序,该应用程序采用鼠标坐标并根据坐标缩放矩形。
我面临的问题是我不知道如何调用使用更多参数的方法(在本例中为 x
、y
和 PaintEventArgs
)。或者,我知道如何处理 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()
方法根据鼠标坐标用 width
和 height
绘制矩形。
那么我怎样才能用坐标调用 DrawRect()
和 PaintEventArgs
?
PaintEventArgs
允许您访问 Graphics
对象,您需要那个对象来画东西。
如果您不想使用 PaintEventArgs
,我建议您调用 Form
的 CreateGraphics()
方法,它将允许您绘制矩形。
为了提高性能,我建议您使用 using(...){ } 键来处理 Graphics
对象和 Pen
对象。
您需要包含 System.Drawing
才能使用 Graphics
和 Pen
。
您的代码将如下所示:
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);
}
}
}
}
app that takes mouse coordinates and scales rectangle according to the coordinates
我希望看到这样的东西(伪代码):
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()
调用的组合,它引发绘画事件。您使用字段传递的变量。
在 WinForms 应用程序中绘图的方式可能与您预期的略有不同。现在屏幕上的所有内容都被认为是临时的,例如,如果您最小化并恢复你的 window,屏幕上的内容将被删除,你将被要求重新绘制它(你的 window 的 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);
}
}
在控件的表面上绘图时,您始终使用该控件的 Paint
事件或覆盖 Custom/User 控件的 OnPaint
方法。
不要试图存储它的 Graphics
对象:一旦 Control 无效(重新绘制)它就变得无效。
使用 PaintEventArgs
对象提供的 Graphics
对象。
当需要更复杂的过程来绘制不同的形状时,您可以将 e.Graphics
对象传递给不同的方法,这些方法将使用该对象来执行专门的绘图。
在这个例子中,每个绘制形状的坐标和其他属性被分配给一个专门的class,DrawingRectangle
(这里是一个简化的结构,它可以容纳更复杂的功能)。
List<DrawingRectangle>()
存储在 会话 中生成的所有 DrawingRectangle
对象的引用(直到清除绘图)。
每次在控件的表面上生成 Left MouseDown
事件时,都会将一个新的 DrawingRectangle
对象添加到列表中。
e.Location
存储为 DrawingRectangle.StartPoint
(一个不变的值)和 DrawingRectangle.Location
:当鼠标指针移动时该值将更新。
当鼠标移动时,当前e.Location
值将从先前存储的起点坐标中减去。一个简单的计算允许从各个方面绘制形状。
此度量决定了矩形的当前大小。
要从绘图中删除矩形,您只需从列表中删除它的引用和Invalidate()
提供绘图表面的控件。
要清除绘图表面,请清除 List<DrawingRectangle>()
(drawingRects.Clear()
) 并调用 Invalidate()
.
这里还有一些其他例子:
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();
}
显然,我最近开始在 C#
编程,并且正在尝试做一个简单的 WinForms
应用程序,该应用程序采用鼠标坐标并根据坐标缩放矩形。
我面临的问题是我不知道如何调用使用更多参数的方法(在本例中为 x
、y
和 PaintEventArgs
)。或者,我知道如何处理 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()
方法根据鼠标坐标用 width
和 height
绘制矩形。
那么我怎样才能用坐标调用 DrawRect()
和 PaintEventArgs
?
PaintEventArgs
允许您访问 Graphics
对象,您需要那个对象来画东西。
如果您不想使用 PaintEventArgs
,我建议您调用 Form
的 CreateGraphics()
方法,它将允许您绘制矩形。
为了提高性能,我建议您使用 using(...){ } 键来处理 Graphics
对象和 Pen
对象。
您需要包含 System.Drawing
才能使用 Graphics
和 Pen
。
您的代码将如下所示:
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);
}
}
}
}
app that takes mouse coordinates and scales rectangle according to the coordinates
我希望看到这样的东西(伪代码):
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()
调用的组合,它引发绘画事件。您使用字段传递的变量。
在 WinForms 应用程序中绘图的方式可能与您预期的略有不同。现在屏幕上的所有内容都被认为是临时的,例如,如果您最小化并恢复你的 window,屏幕上的内容将被删除,你将被要求重新绘制它(你的 window 的 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);
}
}
在控件的表面上绘图时,您始终使用该控件的 Paint
事件或覆盖 Custom/User 控件的 OnPaint
方法。
不要试图存储它的 Graphics
对象:一旦 Control 无效(重新绘制)它就变得无效。
使用 PaintEventArgs
对象提供的 Graphics
对象。
当需要更复杂的过程来绘制不同的形状时,您可以将 e.Graphics
对象传递给不同的方法,这些方法将使用该对象来执行专门的绘图。
在这个例子中,每个绘制形状的坐标和其他属性被分配给一个专门的class,DrawingRectangle
(这里是一个简化的结构,它可以容纳更复杂的功能)。
List<DrawingRectangle>()
存储在 会话 中生成的所有 DrawingRectangle
对象的引用(直到清除绘图)。
每次在控件的表面上生成 Left MouseDown
事件时,都会将一个新的 DrawingRectangle
对象添加到列表中。
e.Location
存储为 DrawingRectangle.StartPoint
(一个不变的值)和 DrawingRectangle.Location
:当鼠标指针移动时该值将更新。
当鼠标移动时,当前e.Location
值将从先前存储的起点坐标中减去。一个简单的计算允许从各个方面绘制形状。
此度量决定了矩形的当前大小。
要从绘图中删除矩形,您只需从列表中删除它的引用和Invalidate()
提供绘图表面的控件。
要清除绘图表面,请清除 List<DrawingRectangle>()
(drawingRects.Clear()
) 并调用 Invalidate()
.
这里还有一些其他例子:
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();
}