建议:在图片框之间拖放

Suggestion: Drag and Dropping Between PictureBoxes

我包含了 2 张图片,以便您更容易理解我的问题, 由于我是新会员,我不能直接 link 它们在这里,所以我把它们放在我的 Dropbox public 文件夹中,

图 1:设计视图 https://www.dropbox.com/s/wca5gstd8kdsie7/designView.png?dl=0

在这里你可以看到左边有一个群组面板, 嵌套了 6 个较小的盒子,它们都是 "pictureBoxes",

然后右边是一个大一点的图片Box,里面嵌套了一个小一点的,

图2应用运行 https://www.dropbox.com/s/u5bknooks17of1r/appView.png?dl=0

在这里你可以看到左边加载的图片(请忽略图片的失真,这是我稍后会修复的)

我需要做的是,将一些图像(短语)从左侧拖到右侧的圆圈中,在 "segments"、

然后我想保存这个圆圈,将新图像拖到它上面(我会做一个 drawToBitmap),我只是想知道什么是我做这个的最好方法?

请提出一些建议,

我现在可以移动左边的图片,但只能移动很小的一部分,因为它们目前仅限于在图片框内移动,

您的问题包括三个任务:

  • 移动Controls(此处:PictureBoxes)。
  • 正在查看他们降落的位置
  • 以让他们加入目标区域的方式放置他们

以下是关于每项任务的一些提示:

  • 要移动Controls,最好使用MouseDown存储起点,使用MouseMove事件更新移动控件的Location。在 MouseUp 事件中,您可以完成操作:可能处理命中、重置控件、输出用户消息等。

请注意,事件属于您要移动的每个控件,但如果将它们映射到所有控件的相同三个事件,则可以使用 sender 参数向右移动 Control。比重复相同的代码 n 次要好得多..

(甚至可以编写可重用函数 makeMoveable(Control ctl) 使任何 Control 可移动;这可能涉及三个事件的三个​​ lambda 表达式..)

另请注意,如果您的控件最初位于某个容器内,例如 Panel,您将需要将其 Parent 更改为 Form,这可能是最好的;在这里你还需要通过原始父级的偏移量来调整位置,你需要使用 BringtoFront() 使它们移动到窗体上所有其他控件之上..

  • 对于命中测试,您需要检查新位置是否在圆的一部分内。段是比 Rectangle 更复杂的形状。复杂的形状可以通过在 GraphicsPath 中添加简单的东西来构建。

Rectangles 似乎 更容易进行命中测试,因为它们具有 Rectangle.Contains(Point) 功能。没有特殊原因,GraphicsPaths 的相同函数被命名为 GraphicsPath.IsVisible(Point).

  • 如果你有一个 GraphicsPaths 的列表组成一个完整的圆,你可以使用它来限制绘图并在使用 Graphics.SetClip(segment) 后将正确的图像直接绘制到圆上。

这里有一个函数可以创建一个片段并将其绘制到 PictureBox pb_target 事件中的 PictureBox pb_target 上。请注意,两个 Arcs 的绘制方向相反,因此 Closing Path 不会创建交叉连接!

List<GraphicsPath> segments = new List<GraphicsPath>();


private void Form4_Load(object sender, EventArgs e)
{
     PointF center = new PointF(pb_target.Width / 2f, pb_target.Height / 2f);
     float angle = 60f;
     for (int i = 0; i < 360 / angle; i++)
     {
        segments.Add(getSegment(center, pb_target.Width / 2.5f, 
                                pb_target.Width / 4f, i * angle, angle));
     }

}

GraphicsPath getSegment(PointF center, float radius, float width, 
                        float startAngle, float angle)
{
    GraphicsPath gp = new GraphicsPath();
    float radI = radius - width;
    RectangleF OunterBounds = 
      new RectangleF(center.X - radius, center.Y - radius, 2 * radius, 2 * radius);
    RectangleF InnerBounds = 
      new RectangleF(center.X - radI, center.Y - radI, 2 * radI, 2 * radI);

    gp.AddArc(OunterBounds, startAngle, angle);
    gp.AddArc(InnerBounds, startAngle + angle, -angle);
    gp.CloseFigure();

    return gp;
}

private void pb_target_Paint(object sender, PaintEventArgs e)
{
    for (int i = 0; i < segments.Count; i++)
    {
        GraphicsPath gp = segments[i];
        e.Graphics.FillPath(Brushes.Gainsboro, gp);
        e.Graphics.DrawPath(Pens.SlateBlue, gp);
    }

}

在最终版本中您不需要上面的绘画代码,但它会帮助您微调坐标,即 getSegment 例程中的中心和大小..

此处严格用于测试的是目标 PB 的 MouseMove 事件:

private void pb_target_MouseMove(object sender, MouseEventArgs e)
{
    for (int i = 0; i < segments.Count; i++)
    {
        GraphicsPath gp = segments[i];
        if (gp.IsVisible(e.Location))
        {
            Text = "Inside segment #" + i; 
            break;
        }
        else Text = "Outside of the Circle";

    }
}

注意段从左边顺时针计数..