绘制线将一个树视图的树视图节点链接到另一个树视图的树视图节点

时间:2015-08-06 17:25:36

标签: c# winforms treeview drawing paint

如何绘制一条线以将树视图节点链接到另一个树视图节点

  

链接应显示在

1 个答案:

答案 0 :(得分:4)

WinForms TreeViews中很特别。

  • 对于他们没有Paint事件的人,所以无法在他们身上画画。 (您可以将它们子类化,请参阅下面的更新..!)

  • 其次,您无法在其中嵌套透明控件。你可以嵌套它,但它不会透明。)

所以吸引TreeView似乎是不可能的。但也许它不是你想要的......?

我们改为在两个TreeViews之间划线,连接两个TreeNodes n1和n2。

我们将电视放在Panel panel1上。

为了测试我创建了两个类级别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);
}

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

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);

enter image description here

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

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

enter image description here

此屏幕截图使用TreeView 子类并绘制三行:每个电视一个,下面面板一个。

以下是TreeView类:

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);
        }
    }
  }

如果电视是另一个电视的左侧或右侧及其BorderWidth,则公开bool以设置bool,以及确定哪个节点应与线路连接的方法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);
}