c# - 如何在 C# 中拖动和移动形状

标签 c# winforms user-interface drawing gdi+

在 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,然后使用 GraphicsPath.IsVisible方法或GraphicsPath.IsOutlineVisible方法执行 HitTest 。

  • 要检查某个点是否位于路径区域(例如填充形状),请使用 IsVisible

  • 要对直线、曲线或空形状进行 HitTest ,您可以使用 IsOutlineVisible

示例

例如,您可以创建一个基本 IShape 接口(interface),其中包含 HitTest 、绘图和移动的方法。然后在类中实现这些方法。您还可以创建一个 DrawingSurface 控件,它可以处理 HitTest 、绘制和移动 IShape 对象。

在下面的示例中,我们创建了 IShape 接口(interface)、LineCircle 类。我们还创建了一个DrawingSurface 控件。要测试示例,只需将 DrawingSurface 控件放在 Form 上并处理表单的 Load 事件并添加一些形状,然后运行应用程序就足够了并尝试移动形状。

IShape

该接口(interface)包含一些有用的方法,如果任何类实现了这些方法,则可用于绘图、 HitTest 和移动。在此示例的末尾,您可以看到一个 DrawingSurface 控件,它可以简单地与 IShape 实现一起使用:

public interface IShape
{
    GraphicsPath GetPath();
    bool HitTest(Point p);
    void Draw(Graphics g);
    void Move(Point d);
}

线条

这是一个实现IShape接口(interface)的线路类。进行 HitTest 时,如果单击该行,HitTest 将返回 true。另外为了让你更简单地选择线路,我添加了 2 个点用于 HitTest :

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接口(interface)的圆类。进行 HitTest 时,如果单击圆圈,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 中执行点击测试,并在拖动形状时移动形状。您应该将一些形状(例如 LineCircle)添加到控件的 Shapes 集合中。

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# - 如何在 C# 中拖动和移动形状,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38747027/

相关文章:

c# - 获取抛出异常的方法名

c# - 将音频输入转换为时间并使用C#显示

user-interface - 响应式 UI - 修改屏幕阅读器中的阅读顺序?

android - 带步骤的注册 GUI

c# - 在 C# WinForm 应用程序中嵌入 MS Access 数据库

JavaFX:如何为组合框设置操作?

c# - 有什么办法可以绕过 RenderBody() 要求吗?

c# - EventTrigger 绑定(bind)到来自 DataContext 的事件

c# - 局部变量错误

c# - BackgroundWorker 问题(C# Windows 窗体)