如何在运行时连接 shapes/labels 行?

How to connect with line shapes/labels on runtime?

我想在运行时创建 labels/shapes,然后像在 Visio 中那样用线连接形状。

使用此代码,我正在创建 "block":

    private void createBlock() {
                try {
                    Label label = new Label();
                    label.AutoSize = false;
                    label.Location = Control.MousePosition;
                    label.Size = new Size(89, 36);
                    label.BackColor = Color.DarkOliveGreen;
                    label.ForeColor = Color.White;
                    label.FlatStyle = FlatStyle.Flat;
                    label.TextAlign = ContentAlignment.MiddleCenter;
                    label.Text = "New Block";
                    label.ContextMenuStrip = contextBlock;
                    canvas.Controls.Add(label);
                    MoveBlock(label);                
                } catch (Exception ex) {
                    MessageBox.Show(ex.Message);
                }
            }

有了这个,我正在移动以下形式的对象:

private void MoveBlock(Label block, Label endBlock=null){
                 block.MouseDown += (ss, ee) => {
                    if (ee.Button == System.Windows.Forms.MouseButtons.Left) fPoint = Control.MousePosition;                     
                };
                 block.MouseMove += (ss, ee) => {
                     if (ee.Button == System.Windows.Forms.MouseButtons.Left) {
                         Point temp = Control.MousePosition;
                         Point res = new Point(fPoint.X - temp.X, fPoint.Y - temp.Y);

                         block.Location = new Point(block.Location.X - res.X, block.Location.Y - res.Y);
                         fPoint = temp;

                     }
                 }; 
            }

我该怎么做?至少如何搜索它?做这个的最好方式是什么?

这是一个最小的示例,您可以如何从稍微更改和扩展代码开始:

首先我们创建一个 class 级别变量来保存连接块的列表:

List<Tuple<Label, Label>> lines = new List<Tuple<Label, Label>>();

您可能想使用自己的 class 而不是便宜的 Tuples 来保存有关线条的更多信息,例如颜色、笔型、宽度等。

接下来我们把createBlock方法return做成新的Label,这样我们就可以直接使用了..

private Label createBlock()          <---- for convenience!
{
    try
    {
        Label label = new Label();
        label.AutoSize = false;
        label.Location = Control.MousePosition;
        label.Size = new Size(89, 36);
        label.BackColor = Color.DarkOliveGreen;
        label.ForeColor = Color.White;
        label.FlatStyle = FlatStyle.Flat;
        label.TextAlign = ContentAlignment.MiddleCenter;
        label.Text = "New Block";
        label.ContextMenuStrip = contextBlock;
        canvas.Controls.Add(label);
        MoveBlock(label);
        return label;                <---- for convenience!
    } catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    return null;
}

以下是我在 Form Load 活动中使用它的方法:

private void Form1_Load(object sender, EventArgs e)
{
    Label l1 = createBlock();
    Label l2 = createBlock();

    lines.Add(new Tuple<Label, Label>(l1,l2));
}

最好将每个块放入 List<Label> 或(List<Block> 一旦您将它们升级到 class Block)以访问它们稍后,这样您就不必费力地浏览 canvas.Controls 集合..

为了让它显示线条,我们需要在每次添加或移动线条时触发 Paint 事件:

private void MoveBlock(Label block, Label endBlock = null)
{
    block.MouseDown += (ss, ee) =>
    {
        if (ee.Button == System.Windows.Forms.MouseButtons.Left) 
            fPoint = Control.MousePosition;
    };
    block.MouseMove += (ss, ee) =>
    {
        if (ee.Button == System.Windows.Forms.MouseButtons.Left)
        {
            Point temp = Control.MousePosition;
            Point res = new Point(fPoint.X - temp.X, fPoint.Y - temp.Y);

            block.Location = new Point(block.Location.X - res.X,
                                       block.Location.Y - res.Y);
            fPoint = temp;
            canvas.Invalidate();   // <------- draw the new lines
        }
    };
}

我希望你的 canvas 是双缓冲的(或者 canvas 是 PictureBox)!

下面是绘制线条的简单实现:

private void canvas_Paint(object sender, PaintEventArgs e)
{
    foreach (Tuple<Label, Label> t in lines)
    {
        Point p1 = new Point(t.Item1.Left + t.Item1.Width / 2, 
                             t.Item1.Top + t.Item1.Height / 2);
        Point p2 = new Point(t.Item2.Left + t.Item2.Width / 2, 
                             t.Item2.Top + t.Item2.Height / 2);

        e.Graphics.DrawLine(Pens.Black, p1, p2);
    }
}

有很多地方需要改进,但它确实有效而且相当简单。线条在标签后面,只要您不过度拥挤canvas,它们看起来就很自然..

关于 Winforms Graphics 有很多东西需要学习,但它们远远超出了本文的范围 post..