2

Is there a way to customize a winform treeview to get something like ?

enter image description here

The purpose is to have one color by parent item an define a triangle instead of +/- icons to develop item.

A.Pissicat
  • 3,023
  • 4
  • 38
  • 93
  • 1
    `treeView1.DrawMode = TreeViewDrawMode.OwnerDrawAll;` – LarsTech Mar 22 '17 at 14:35
  • A [recent question](https://stackoverflow.com/questions/57108229/why-the-code-never-reaching-the-override-ondrawnode-method) had problems with the answer here so I posted a more complete solution there. – György Kőszeg Jul 19 '19 at 12:52

2 Answers2

2

Use TreeViewDrawMode.OwnerDrawText so the indenting will be adjusted by the TreeView. Apart from that, you should implement the full painting.

public sealed class AdvancedTreeView : TreeView
{
    public AdvancedTreeView()
    {
        DrawMode = TreeViewDrawMode.OwnerDrawText;
        ShowLines = false;
        AlternateBackColor = BackColor;
    }

    public Color AlternateBackColor { get; set; }

    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
        // background
        Color backColor = (GetTopNodeIndex(e.Node) & 1) == 0 ? BackColor : AlternateBackColor;
        using (Brush b = new SolidBrush(backColor))
        {
            e.Graphics.FillRectangle(b, new Rectangle(0, e.Bounds.Top, ClientSize.Width, e.Bounds.Height));
        }

        // icon
        if (e.Node.Nodes.Count > 0)
        {
            Image icon = GetIcon(e.Node.IsExpanded); // TODO: true=down;false:right
            e.Graphics.DrawImage(icon, e.Bounds.Left - icon.Width - 3, e.Bounds.Top);
        }

        // text (due to OwnerDrawText mode, indenting of e.Bounds will be correct)
        TextRenderer.DrawText(e.Graphics, e.Node.Text, Font, e.Bounds, ForeColor);

        // indicate selection (if not by backColor):
        if ((e.State & TreeNodeStates.Selected) != 0)
            ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds);
    }

    private int GetTopNodeIndex(TreeNode node)
    {
        while (node.Parent != null)
            node = node.Parent;

        return Nodes.IndexOf(node);
    }
}

To get the result similar to your screenshot just set the colors and you are done.

advancedTreeView1.BackColor = Color.DeepSkyBlue;
advancedTreeView1.AlternateBackColor = Color.LightBlue;
György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
  • I've try your solution but i maybe missed something. I created an `AdvancedTreeView`, I added 10 nodes with child (tested before with a standard `TreeView`) but it only display a background DeepSkyBlue colored. I added a breakpoint in `OnDrawNode`, this function is never called. – A.Pissicat Mar 22 '17 at 15:38
  • It is called if `DrawMode` is anything but `Normal`. – György Kőszeg Mar 22 '17 at 15:53
  • I modified my tree, now it's working (probably node was not added). Thank you – A.Pissicat Mar 22 '17 at 16:03
  • I modified your function `GetIcon`. Now the triangle is draw and we can define a color and a margin. It is more reusable to my opinion. – A.Pissicat Mar 22 '17 at 17:28
1

Simply set the DrawMode to "OwnerDrawAll" in the properties of the TreeView. Keep in mind though, that you have to paint it all yourself then and have to handle the TreeView_DrawNode event. Here's an example for the event handling:

private void TreeListView_DrawNode(object sender, DrawTreeNodeEventArgs e)
    {
        if (e.Bounds.Height == 0)
            return;

        e.Graphics.FillRectangle(new SolidBrush((e.Node.Parent?.Index ?? e.Node.Index) % 2 == 0 ? Color.Blue : Color.Aqua), e.Bounds);

        if (e.Node.Nodes.Count > 0)
        {
            if (!e.Node.IsExpanded)
                e.Graphics.FillPolygon(Brushes.Red,
                    new[]
                    {
                        new PointF(e.Bounds.X + e.Bounds.Height / 10, e.Bounds.Y + e.Bounds.Height / 10),
                        new PointF(e.Bounds.X + e.Bounds.Height / 10, e.Bounds.Y + e.Bounds.Height * 0.9f),
                        new PointF(e.Bounds.X + e.Bounds.Height, e.Bounds.Y + e.Bounds.Height / 2)
                    });
            else
                e.Graphics.FillPolygon(Brushes.Red,
                    new[]
                    {
                        new PointF(e.Bounds.X + e.Bounds.Height / 10, e.Bounds.Y + e.Bounds.Height / 10),
                        new PointF(e.Bounds.X + e.Bounds.Height, e.Bounds.Y + e.Bounds.Height / 10),
                        new PointF(e.Bounds.X + e.Bounds.Height / 2, e.Bounds.Y + e.Bounds.Height)
                    });
        }
        e.Graphics.DrawString(e.Node.Text, new Font(FontFamily.GenericMonospace, e.Bounds.Height * 0.7f),
            new SolidBrush(Color.Black),
            new Rectangle(e.Bounds.X + e.Bounds.Height, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
    }
MetaColon
  • 2,895
  • 3
  • 16
  • 38
  • Is there a difference between `TextRenderer.DrawText` and `e.Graphics.DrawString` ? – A.Pissicat Mar 23 '17 at 08:47
  • There is - take a look at this: http://stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality – MetaColon Mar 23 '17 at 08:53