如何在 C# 中拖动和移动形状
Posted
技术标签:
【中文标题】如何在 C# 中拖动和移动形状【英文标题】:How to drag and move shapes in C# 【发布时间】:2016-12-09 09:04:06 【问题描述】:在 C# WindoeFormsApplication 中,是否可以选择,从而用鼠标移动或删除绘制的形状?就像 windows 画图程序一样。
形状绘图工作得很好,所有点都存储在某个数组中。作为这个画线的例子
Point Latest get; set;
List<Point> _points = new List<Point>();
protected override void OnMouseMove(MouseEventArgs e)
base.OnMouseMove(e);
// Save the mouse coordinates
Latest = new Point(e.X, e.Y);
// Force to invalidate the form client area and immediately redraw itself.
Refresh();
protected override void OnPaint(PaintEventArgs e)
var g = e.Graphics;
base.OnPaint(e);
if (_points.Count > 0)
var pen = new Pen(Color.Navy);
var pt = _points[0];
for(var i=1; _points.Count > i; i++)
var next = _points[i];
g.DrawLine(pen, pt, next);
pt = next;
g.DrawLine(pen, pt, Latest);
private void Form1_MouseClick(object sender, MouseEventArgs e)
Latest = new Point(e.X, e.Y);
_points.Add(Latest);
Refresh();
我可以让它通过基本线性代数计算鼠标位置到每条线的最短距离,并设置一个阈值距离,如果小于阈值,则选中这条线,并且可以用鼠标拖动或编辑。但是,只是想知道,对于这样的任务,有什么方法更易于管理吗?主要是选择部分。 任何建议将不胜感激,谢谢!
【问题讨论】:
你不需要线性代数。您可以使用GraphicsPath.IsVisible
来检查一个点是否在路径中。
【参考方案1】:
要达到测试形状,您不需要线性代数。您可以为您的形状创建GraphicsPath
,然后使用GraphicsPath.IsVisible
方法或GraphicsPath.IsOutlineVisible
方法执行命中测试。
要检查一个点是否在您的路径区域中,例如填充形状,请使用IsVisible
。
要对直线、曲线或空形状进行命中测试,您可以使用IsOutlineVisible
。
示例
例如,您可以创建一个基本的IShape
接口,其中包含用于命中测试、绘图和移动的方法。然后在类中实现这些方法。您还可以创建一个DrawingSurface
控件,它可以处理命中测试、绘制和移动IShape
对象。
在下面的示例中,我们创建了IShape
接口、Line
和Circle
类。我们还创建了一个DrawingSurface
控件。为了测试这个例子,只需在Form
上放置一个DrawingSurface
控件并处理Load
表单事件并添加一些形状,然后运行应用程序并尝试移动形状。
IShape
这个接口包含一些有用的方法,如果有任何类实现它们,可以用于绘图、命中测试和移动。在本示例的最后,您可以看到一个 DrawingSurface
控件,它可以简单地与 IShape
实现一起使用:
public interface IShape
GraphicsPath GetPath();
bool HitTest(Point p);
void Draw(Graphics g);
void Move(Point d);
线
这是一个实现IShape
接口的行类。在点击在线进行命中测试时,HitTest
返回 true。另外为了让你选线更简单,我加了 2 分进行命中测试:
public class Line : IShape
public Line() LineWidth = 2; LineColor = Color.Black;
public int LineWidth get; set;
public Color LineColor get; set;
public Point Point1 get; set;
public Point Point2 get; set;
public GraphicsPath GetPath()
var path = new GraphicsPath();
path.AddLine(Point1, Point2);
return path;
public bool HitTest(Point p)
var result = false;
using (var path = GetPath())
using (var pen = new Pen(LineColor, LineWidth + 2))
result = path.IsOutlineVisible(p, pen);
return result;
public void Draw(Graphics g)
using (var path = GetPath())
using (var pen = new Pen(LineColor, LineWidth))
g.DrawPath(pen, path);
public void Move(Point d)
Point1 = new Point(Point1.X + d.X, Point1.Y + d.Y);
Point2 = new Point(Point2.X + d.X, Point2.Y + d.Y);
圈子
这是一个实现IShape
接口的圆类。点击圆圈进行命中测试时,HitTest
返回 true:
public class Circle : IShape
public Circle() FillColor = Color.Black;
public Color FillColor get; set;
public Point Center get; set;
public int Radious get; set;
public GraphicsPath GetPath()
var path = new GraphicsPath();
var p = Center;
p.Offset(-Radious, -Radious);
path.AddEllipse(p.X, p.Y, 2 * Radious, 2 * Radious);
return path;
public bool HitTest(Point p)
var result = false;
using (var path = GetPath())
result = path.IsVisible(p);
return result;
public void Draw(Graphics g)
using (var path = GetPath())
using (var brush = new SolidBrush(FillColor))
g.FillPath(brush, path);
public void Move(Point d)
Center = new Point(Center.X + d.X, Center.Y + d.Y);
绘图表面
控件,绘制形状列表。它还在MouseDown
中执行命中测试,并在拖动时移动形状。您应该在控件的Shapes
集合中添加一些形状,例如Line
或Circle
。
public class DrawingSurface : Control
public List<IShape> Shapes get; private set;
IShape selectedShape;
bool moving;
Point previousPoint = Point.Empty;
public DrawingSurface() DoubleBuffered = true; Shapes = new List<IShape>();
protected override void OnMouseDown(MouseEventArgs e)
for (var i = Shapes.Count - 1; i >= 0; i--)
if (Shapes[i].HitTest(e.Location)) selectedShape = Shapes[i]; break;
if (selectedShape != null) moving = true; previousPoint = e.Location;
base.OnMouseDown(e);
protected override void OnMouseMove(MouseEventArgs e)
if (moving)
var d = new Point(e.X - previousPoint.X, e.Y - previousPoint.Y);
selectedShape.Move(d);
previousPoint = e.Location;
this.Invalidate();
base.OnMouseMove(e);
protected override void OnMouseUp(MouseEventArgs e)
if (moving) selectedShape = null; moving = false;
base.OnMouseUp(e);
protected override void OnPaint(PaintEventArgs e)
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
foreach (var shape in Shapes)
shape.Draw(e.Graphics);
【讨论】:
你也会发现这篇文章很有帮助Draw multiple freehand Polyline or Curve drawing - Adding Undo Feature 还有这个c# How do I detect if a line (painted/drawn on a form) has been clicked on when using winforms?以上是关于如何在 C# 中拖动和移动形状的主要内容,如果未能解决你的问题,请参考以下文章