将一个树视图的 link 树视图节点画线到另一个树视图的树视图节点

Drawing line to link treeview node of one treeview to treeview node of another treeview

如何画一条线到 link 树视图节点到另一个树视图节点

link should be shown in from

WinFormsTreeViews是特殊的。

  • 他们没有 Paint 事件,因此无法利用它们。 (不过你可以子class他们,看下面的更新..!)

  • 其次不能在其中嵌套透明控件。你可以嵌套它但它不会透明..)

所以在 TreeView 上绘图似乎是不可能的。但也许这不是你想要的..?

让我们画一条线两个TreeViews之间,连接两个TreeNodesn1和n2。

让我们把电视放在 Panel panel1 上。

为了测试,我创建了两个 class 级别 Points p1 and p2:

Point p1 = Point.Empty;
Point p2 = Point.Empty;

Panel's Paint 事件中我们画线:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.DrawLine(Pens.Firebrick, p1, p2);
}

为了测试,我在 NodeMouseClick 事件中设置了 Points

private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
    TreeNode n1 = e.Node;
    // for testing I search for a corresponding node:
    TreeNode n2 = treeView2.Nodes.Find(n1.Name, true).First();
    // for testing I select the node:
    treeView2.SelectedNode = n2;
    // top left points in the node:
    p1 = n1.Bounds.Location;
    p2 = n2.Bounds.Location;
    // add the offset of the treviews:
    p1.Offset(treeView1.Left, treeView1.Top);
    p2.Offset(treeView2.Left, treeView2.Top);
    // trigger the paint event;
    panel1.Invalidate();
}

注意上面的代码连接了节点的左上角

要连接各个线的外侧,您可以这样计算点:

p1 = new Point(treeView1.Right, n1.Bounds.Top + n1.Bounds.Height / 2 + treeView1.Top);
p2 = new Point(treeView2.Left, n2.Bounds.Top + n2.Bounds.Height / 2 + treeView2.Top);

更新: 非常感谢 Larstech 提供有关覆盖 WndProc 方法和捕获 WM_PAINT 的信息。我倾向于屏蔽 WndProc ;-)

使用这种技术确实可以绘制到 TreeView 上:

此屏幕截图使用 TreeView subclass 并绘制三行:每台电视上一行,下方面板上一行。

这里是 TreeView class:

class PTreeView : TreeView
{
    public bool IsLeft { get; set; }
    public int  BorderWidth { get; private set; }
    private float slope { get; set; }
    private Point Pt { get; set; }

    public PTreeView()     {       }

    public void markNode(TreeNode node, float slope_)
    {
        if (this.IsLeft ) Pt = 
         new Point(node.Bounds.Right, node.Bounds.Top + node.Bounds.Height / 2);
        else Pt = new Point(node.Bounds.Left, node.Bounds.Top + node.Bounds.Height / 2);
        slope = slope_;
        BorderWidth = (this.Width - this.ClientRectangle.Width) / 2;
    }

    internal const int WM_PAINT = 0xF;
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == WM_PAINT)
        {
            Graphics G = this.CreateGraphics();
            G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            int px = IsLeft ? this.ClientRectangle.Width : 0;
            int py = (int)(Pt.Y + slope * (Pt.X - px));
            Point p0 = new Point(px, py);

            G.DrawLine(Pens.Coral, Pt, p0);
        }
    }
  }

它公开了一个 bool 来设置电视是在另一台电视的左边还是右边及其 BorderWidth,以及一个方法 markNode 确定哪个节点应该与线路连接以及什么直线的斜率。

NodeMouseClick 扩展了一点:

private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
    TreeNode n1 = e.Node;
    TreeNode n2 = treeView2.Nodes.Find(n1.Name, true).First();
    treeView2.SelectedNode = n2;

    p1 = new Point(
          treeView1.Left + n2.Bounds.Left + n1.Bounds.Width + treeView1.BorderWidth,
          treeView1.Top  + n1.Bounds.Top + n1.Bounds.Height / 2 + treeView1.BorderWidth);
    p2 = new Point(
          treeView2.Left + n2.Bounds.Left + treeView2.BorderWidth,
          treeView2.Top  + n2.Bounds.Top + n2.Bounds.Height / 2 + treeView2.BorderWidth);

    float slope = -1f * (p2.Y - p1.Y) / (p2.X - p1.X);
    treeView1.markNode(n1, slope);
    treeView2.markNode(n2, slope);

    panel1.Invalidate();
    treeView1.Invalidate();
    treeView2.Invalidate();
}

它现在计算斜率并在两个树视图上同时调用 markNodeInvalidate..

Panel Paint 没有真正的变化:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
    e.Graphics.DrawLine(Pens.Coral, p1, p2);
}