Daniel Lip
Daniel Lip

Reputation: 11317

How can I highlight a node in a treeview when selecting it?

The problem is that inside OnDrawNode I'm using specific background and alternative colors already :

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

public class AdvancedTreeView : TreeView
{
    private Bitmap openedIcon, closedIcon;
    private List<TreeNode> rootNodes = new List<TreeNode>();

    public AdvancedTreeView()
    {
        DrawMode = TreeViewDrawMode.OwnerDrawText;
        ShowLines = false;
        AlternateBackColor = BackColor;
        ArrowColor = SystemColors.WindowText;
        this.AllowDrop = true;  
    }

    public Color AlternateBackColor { get; set; }
    public Color ArrowColor { get; set; }

    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
        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);
            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);

    }

And since it's keep doing calling OnDrawNode all the time it will never do the part inside the OnAfterSelect :

protected override void OnAfterSelect(TreeViewEventArgs e)
    {
        e.Node.BackColor = Color.Green;
    }

Once I click on a node and selecting it, It will also go to the OnDrawNode and will color the node/s in it's original colors again.

I want to keep the code in the OnDrawNode but also to be able to highlight the selected node/s.

EDIT : This is what I have tried so far :

Inside the OnDrawNode :

Color backColor = (GetTopNodeIndex(e.Node) & 1) == 0 ? BackColor : AlternateBackColor;
        if ((e.State & TreeNodeStates.Selected) != 0)
        {
            e.Graphics.FillRectangle(Brushes.Green, e.Bounds);
        }
        else
        {
            using (Brush b = new SolidBrush(backColor))
            {
                e.Graphics.FillRectangle(b, new Rectangle(0, e.Bounds.Top, ClientSize.Width, e.Bounds.Height));
            }
        }

But there is two problem with this :

  1. In the two screenshots when it's on Node10 it looks like fine but when it's on Node9 you can it's coloring the whole line. And I wanted to highlight only the text "New Node 10" ... "New Node 9" and not the whole line.

  2. When selecting a node on the red line triangle there is something small in white color. It wasn't like that before changing the code in the OnDrawNode.

Selected Node 10

And the second screenshot :

Selected Node 9

This is the full code of the TreeView control :

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

public class AdvancedTreeView : TreeView
{
    private Bitmap openedIcon, closedIcon;
    private List<TreeNode> rootNodes = new List<TreeNode>();

    public AdvancedTreeView()
    {
        DrawMode = TreeViewDrawMode.OwnerDrawText;
        ShowLines = false;
        AlternateBackColor = BackColor;
        ArrowColor = SystemColors.WindowText;
        this.AllowDrop = true;
    }

    public Color AlternateBackColor { get; set; }
    public Color ArrowColor { get; set; }

    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
        Color backColor = (GetTopNodeIndex(e.Node) & 1) == 0 ? BackColor : AlternateBackColor;
        if ((e.State & TreeNodeStates.Selected) != 0)
        {
            e.Graphics.FillRectangle(Brushes.Green, e.Bounds);
        }
        else
        {
            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);
            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);
    }

    protected override void OnItemDrag(ItemDragEventArgs e)
    {
        // Move the dragged node when the left mouse button is used.
        if (e.Button == MouseButtons.Left)
        {
            DoDragDrop(e.Item, DragDropEffects.Move);
        }

        // Copy the dragged node when the right mouse button is used.
        else if (e.Button == MouseButtons.Right)
        {
            DoDragDrop(e.Item, DragDropEffects.Copy);
        }
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        // Retrieve the client coordinates of the mouse position.
        Point targetPoint = this.PointToClient(new Point(e.X, e.Y));

        // Select the node at the mouse position.
        this.SelectedNode = this.GetNodeAt(targetPoint);
    }

    protected override void OnDragDrop(DragEventArgs e)
    {
        Point targetPoint = PointToClient(new Point(e.X, e.Y));
        TreeNode targetNode = GetNodeAt(targetPoint);
        TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
        if (draggedNode == null || targetNode == null || draggedNode.Level != targetNode.Level)
        {
            return;
        }
        else
        {
            TreeNode parentNode = targetNode;
            if (!draggedNode.Equals(targetNode) && targetNode != null)
            {
                bool canDrop = true;
                while (canDrop && (parentNode != null))
                {
                    canDrop = !Object.ReferenceEquals(draggedNode, parentNode);
                    parentNode = parentNode.Parent;
                }
                if (canDrop)
                {
                    TreeNode treeNode = draggedNode.Parent;
                    if (treeNode != null)
                    {
                        int index = draggedNode.Index;
                        draggedNode.Remove();
                        treeNode.Nodes.Insert(targetNode.Index, draggedNode);
                        targetNode.Remove();
                        treeNode.Nodes.Insert(index, targetNode);
                    }
                    else
                    {
                        int draggedindex = draggedNode.Index;
                        int targetindex = targetNode.Index;
                        draggedNode.Remove();
                        targetNode.Remove();
                        this.Nodes.Insert(targetindex, draggedNode);
                        this.Nodes.Insert(draggedindex, targetNode);
                    }
                }
            }
        }
        SelectedNode = draggedNode;
    }

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

        return Nodes.IndexOf(node);
    }

    // Determine whether one node is a parent 
    // or ancestor of a second node.
    private bool ContainsNode(TreeNode node1, TreeNode node2)
    {
        // Check the parent node of the second node.
        if (node2.Parent == null) return false;
        if (node2.Parent.Equals(node1)) return true;

        // If the parent node is not null or equal to the first node, 
        // call the ContainsNode method recursively using the parent of 
        // the second node.
        return ContainsNode(node1, node2.Parent);
    }

    private Image GetIcon(bool nodeIsExpanded)
    {
        if (openedIcon == null)
            InitIcons();
        return nodeIsExpanded ? openedIcon : closedIcon;
    }

    private void InitIcons()
    {
        openedIcon = new Bitmap(16, 16);
        closedIcon = new Bitmap(16, 16);
        using (Brush b = new SolidBrush(ArrowColor))
        {
            using (Graphics g = Graphics.FromImage(openedIcon))
                g.FillPolygon(b, new[] { new Point(0, 0), new Point(15, 0), new Point(8, 15), });
            using (Graphics g = Graphics.FromImage(closedIcon))
                g.FillPolygon(b, new[] { new Point(0, 0), new Point(15, 8), new Point(0, 15), });
        }
    }
}

Upvotes: 1

Views: 267

Answers (1)

LarsTech
LarsTech

Reputation: 81620

Just check to see if the node is highlighted like you do with the Focus Rectangle:

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));
}
if ((e.State & TreeNodeStates.Selected) != 0) {
  e.Graphics.FillRectangle(Brushes.Green, e.Bounds);
}

Upvotes: 1

Related Questions