如何在 C# 中拖动和移动形状
How to drag and move shapes in C#
在 C# WindoeFormsApplication 中,是否可以 select,从而用鼠标移动或删除绘制的形状?喜欢 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();
}
我可以让它通过基本线性代数计算鼠标位置到每条线的最短距离,并设置一个阈值距离,如果小于阈值,就把这条线selected,就可以了用鼠标拖动或编辑。但是,只是想知道,对于这样的任务,有什么方法更易于管理吗?主要是 selection 部分。
任何建议将不胜感激,谢谢!
要命中测试形状,您不需要线性代数。您可以为形状创建 GraphicsPath
,然后使用 GraphicsPath.IsVisible
method or GraphicsPath.IsOutlineVisible
方法执行命中测试。
要检查某个点是否在您的路径区域中,例如填充形状,请使用 IsVisible
。
要对直线或曲线或空形状进行命中测试,您可以使用IsOutlineVisible
。
例子
例如,您可以创建一个基础 IShape
接口,其中包含用于命中测试、绘图和移动的方法。然后在 classes 中实现这些方法。您还可以创建一个 DrawingSurface
控件,它可以处理命中测试、绘制和移动 IShape
个对象。
在下面的例子中,我们创建IShape
接口,Line
和Circle
classes。我们还创建了一个 DrawingSurface
控件。要测试该示例,只需将 DrawingSurface
控件放在 Form
上并处理表单的 Load
事件并添加一些形状,然后 运行 应用程序并尝试移动形状.
IShape
此接口包含一些有用的方法,如果有 class 实现它们,可用于绘图、命中测试和移动。在此示例的末尾,您可以看到一个 DrawingSurface
控件,它可以简单地与 IShape
实现一起使用:
public interface IShape
{
GraphicsPath GetPath();
bool HitTest(Point p);
void Draw(Graphics g);
void Move(Point d);
}
行
这里有一行 class 实现了 IShape
接口。当命中测试如果你点击在线时,HitTest
returns为真。另外为了让大家选线更简单,我加了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
接口的圆class。当点击圆圈进行点击测试时,HitTest
returns 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);
}
}
在 C# WindoeFormsApplication 中,是否可以 select,从而用鼠标移动或删除绘制的形状?喜欢 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();
}
我可以让它通过基本线性代数计算鼠标位置到每条线的最短距离,并设置一个阈值距离,如果小于阈值,就把这条线selected,就可以了用鼠标拖动或编辑。但是,只是想知道,对于这样的任务,有什么方法更易于管理吗?主要是 selection 部分。 任何建议将不胜感激,谢谢!
要命中测试形状,您不需要线性代数。您可以为形状创建 GraphicsPath
,然后使用 GraphicsPath.IsVisible
method or GraphicsPath.IsOutlineVisible
方法执行命中测试。
要检查某个点是否在您的路径区域中,例如填充形状,请使用
IsVisible
。要对直线或曲线或空形状进行命中测试,您可以使用
IsOutlineVisible
。
例子
例如,您可以创建一个基础 IShape
接口,其中包含用于命中测试、绘图和移动的方法。然后在 classes 中实现这些方法。您还可以创建一个 DrawingSurface
控件,它可以处理命中测试、绘制和移动 IShape
个对象。
在下面的例子中,我们创建IShape
接口,Line
和Circle
classes。我们还创建了一个 DrawingSurface
控件。要测试该示例,只需将 DrawingSurface
控件放在 Form
上并处理表单的 Load
事件并添加一些形状,然后 运行 应用程序并尝试移动形状.
IShape
此接口包含一些有用的方法,如果有 class 实现它们,可用于绘图、命中测试和移动。在此示例的末尾,您可以看到一个 DrawingSurface
控件,它可以简单地与 IShape
实现一起使用:
public interface IShape
{
GraphicsPath GetPath();
bool HitTest(Point p);
void Draw(Graphics g);
void Move(Point d);
}
行
这里有一行 class 实现了 IShape
接口。当命中测试如果你点击在线时,HitTest
returns为真。另外为了让大家选线更简单,我加了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
接口的圆class。当点击圆圈进行点击测试时,HitTest
returns 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);
}
}