33

Is there an easy way to have a selected TreeNode retain its SystemColors.Highlight BackColor while the TreeView doesn't have focus? Because even with HideSelection set to false, the selected BackColor is near impossible to see.

Selected TreeNode while TreeView has focus:

Focused

Selected TreeNode while TreeView does not have focus:

Unfocused

Thanks in advance.

EDIT: I'm aware i could set DrawMode to OwnerDrawAll and then add a custom DrawNode event. I did attempt this previously, the problem i have is i don't know how to go about drawing the TreeNode's corresponding ImageKey properly.

bitbonk
  • 48,890
  • 37
  • 186
  • 278
Spark
  • 1,007
  • 1
  • 8
  • 26

8 Answers8

20

If retaining the SystemColors.Highlight background color is all you wanted, then you don't need to set the TreeView's DrawMode property to TreeViewDrawMode.OwnerDrawAll. Setting it to TreeViewDrawMode.OwnerDrawText should be sufficient, thus you don't need to worry about drawing the TreeNode's corresponding ImageKey.

  1. Set the TreeView.DrawMode to TreeViewDrawMode.OwnerDrawText:

    treeView.DrawMode = TreeViewDrawMode.OwnerDrawText;
    
  2. Set the Treview.HideSelection to false, so that the node states will be kept as selected:

    treeView.HideSelection= false;
    
  3. Add DrawNode event handler to draw the background using SystemColors.Highlight color:

    private void treeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
    {
      if (e.Node == null) return;
    
      // if treeview's HideSelection property is "True", 
      // this will always returns "False" on unfocused treeview
      var selected = (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected;
      var unfocused = !e.Node.TreeView.Focused;
    
      // we need to do owner drawing only on a selected node
      // and when the treeview is unfocused, else let the OS do it for us
      if (selected && unfocused)
      {
        var font = e.Node.NodeFont ?? e.Node.TreeView.Font;
        e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);
        TextRenderer.DrawText(e.Graphics, e.Node.Text, font, e.Bounds, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
      }
      else
      {
        e.DrawDefault = true;
      }
    }
    
IronGeek
  • 4,764
  • 25
  • 27
  • I just figured out how to do it literally 2 minutes before reading your answer, i wanted to accept your answer so i thought i'd better try it first, but it didn't work. The solution can be found above. – Spark Jan 18 '14 at 04:20
  • Oops, you're right. I forgot to add the part about `HideSelection` property. I've updated the answer... – IronGeek Jan 18 '14 at 04:34
  • 4
    Unfortunately that only works properly if FullRowSelect is false, otherwise you get an odd flicker when selecting, and then only the text's background is retained, not the full row. – Spark Jan 18 '14 at 04:50
  • @Spark: I don't see any flickering. But if checkboxes are used, the right border of the checkbox is hidden by the text. (.net 4.8, Win 10 x64). And if FullRowSelect is used (not only enabled, but also used: will only be used if TreeLines are off), the text is blue as desired, but the rest of the row is light gray. – Tobias Knauss Feb 17 '21 at 22:02
15

The solution, works like a charm:

public TreeNode previousSelectedNode = null;
private void treeView1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
    treeView1.SelectedNode.BackColor = SystemColors.Highlight;
    treeView1.SelectedNode.ForeColor = Color.White;
    previousSelectedNode = treeView1.SelectedNode;
}

private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
    if(previousSelectedNode != null)
    {
        previousSelectedNode.BackColor = treeView1.BackColor;
        previousSelectedNode.ForeColor = treeView1.ForeColor;
    }
}
Spark
  • 1,007
  • 1
  • 8
  • 26
  • 1
    FYI this fails when setting the selected item programmatically (solution: call the event manually). – Christian Jan 20 '15 at 11:08
  • 4
    Observe that this only works if HideSelection is True (the default)! – Anders Lindén May 24 '16 at 06:17
  • this didn't work at all for me - the node was still nearly invisible ... the next answer below (handling the drawnode) seems to work perfectly – DanW Nov 01 '17 at 19:39
10

I use this solution, and it's work better

private void treeView1_Enter(object sender, EventArgs e)
{
   if (treeView1.SelectedNode != null)
   {
       treeView1.SelectedNode.BackColor = Color.Empty;
       treeView1.SelectedNode.ForeColor = Color.Empty;
   }
}

private void treeView1_Leave(object sender, EventArgs e)
{
   if (treeView1.SelectedNode != null)
   {
       treeView1.SelectedNode.BackColor = SystemColors.Highlight;
       treeView1.SelectedNode.ForeColor = Color.White;
   }
}

EDIT

Based on this documentation,

The default is Color.Empty

So, in treeView1_Enter it's better to set the color like this

treeView1.SelectedNode.BackColor = Color.Empty;
treeView1.SelectedNode.ForeColor = Color.Empty;

Previous Answer

treeView1.SelectedNode.BackColor = treeView1.BackColor;
treeView1.SelectedNode.ForeColor = treeView1.ForeColor;
Afrig Aminuddin
  • 772
  • 1
  • 9
  • 22
  • Great answer thanks. Note that this only works if HideSelection is True (the default) – Darrel K. May 30 '18 at 07:15
  • 1
    Great answer, thanks! However, it does not work if the selected node is changed (via code) while the tree view is unfocused. This can be fixed by using events BeforeSelect and AfterSelect. – Joh Jan 29 '20 at 16:13
  • The easiest and best-working solution, if combined with the suggestion of @Joh. – Tobias Knauss Feb 17 '21 at 22:10
3

if you just need to highlight selected node when treeview goes out of focus then just use treeView.HideSelection = false;

Actually by default the selected node is highlighted but not visible so we just need to make HideSelection property to false.

Vikash Kumar
  • 129
  • 1
  • 5
  • 1
    The problem, as the question mentions, is that even with HideSelection = false, the selected node is barely distinguishable from other nodes when the tree view doesn't have the focus. – Joh Jan 29 '20 at 11:38
1

Found this great solution here (MSDN)

Worked great for me, hope it'll help somebody else.

  1. Add a new class (file called MyTreeView.cs) to your project and paste the code shown below.
  2. Compile.
  3. Drop the new control called "MyTreeView" from the top of the toolbox onto your form.

You can customize it too, if you know what you are doing

using System;
using System.Drawing;
using System.Windows.Forms;



class MyTreeView : TreeView {
    public MyTreeView() {
        this.DrawMode = TreeViewDrawMode.OwnerDrawText;
    }
    protected override void OnDrawNode(DrawTreeNodeEventArgs e) {
        TreeNodeStates state = e.State;
        Font font = e.Node.NodeFont ?? e.Node.TreeView.Font;
        Color fore = e.Node.ForeColor;
        if (fore == Color.Empty) fore = e.Node.TreeView.ForeColor;
        if (e.Node == e.Node.TreeView.SelectedNode) {
            fore = SystemColors.HighlightText;
            e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);
            ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, fore, SystemColors.Highlight);
            TextRenderer.DrawText(e.Graphics, e.Node.Text, font, e.Bounds, fore, TextFormatFlags.GlyphOverhangPadding);
        }
        else {
            e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
            TextRenderer.DrawText(e.Graphics, e.Node.Text, font, e.Bounds, fore, TextFormatFlags.GlyphOverhangPadding);
        }
    }
}
ukie
  • 188
  • 2
  • 15
0

this is the default behavior for the treeview. All you need to do to change it is override drawnode. There is another stack overflow answer that explains it better than I could here.

How to change background color of selected node in TreeView using TreeViewDrawMode.OwnerDrawAll?

Community
  • 1
  • 1
Allen
  • 97
  • 5
0

Following code works even with programmatically selected nodes.

public TreeNode m_previousSelectedNode = null;
private void m_treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
    if (m_previousSelectedNode != null)
    {
        m_previousSelectedNode.BackColor = m_treeView.BackColor;
        m_previousSelectedNode.ForeColor = m_treeView.ForeColor;
    }

    e.Node.BackColor = SystemColors.Highlight;
    e.Node.ForeColor = Color.White;

    m_previousSelectedNode = m_treeView.SelectedNode;
}
Elephant
  • 675
  • 1
  • 8
  • 18
0

This variant of IronGeeks answer works for me and works with Bold Nodes:

Private Sub TreeView1_DrawNode(sender As Object, e As DrawTreeNodeEventArgs) Handles trvIdent.DrawNode
    Dim treeView = e.Node.TreeView
    Dim selected = (e.Node Is treeView.SelectedNode)
    Dim unfocused = Not treeView.Focused
    Dim font = If(e.Node.NodeFont IsNot Nothing, e.Node.NodeFont, treeView.Font)
    Dim textSize = e.Graphics.MeasureString(e.Node.Text, font)
    Dim bounds = New Rectangle(e.Bounds.X, e.Bounds.Y,
                               Convert.ToInt32(textSize.Width),
                               Math.Max(Convert.ToInt32(textSize.Height), e.Bounds.Height))

    e.DrawDefault = False

    If selected Then
        e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds)
        e.Graphics.DrawString(e.Node.Text, font, SystemBrushes.HighlightText, bounds.Location)
    Else
        e.Graphics.FillRectangle(SystemBrushes.Window, bounds)
        e.Graphics.DrawString(e.Node.Text, font, SystemBrushes.WindowText, bounds.Location)
    End If
    'e.Graphics.DrawRectangle(Pens.Magenta, bounds)
End Sub

The main difference is that the custom drawing is done even if the TreeView is focused. And DrawString() is used instead of DrawText().

dummy
  • 4,256
  • 3
  • 25
  • 36