树节点在树视图未获得焦点时的选中背景色

33

如何让TreeView在失去焦点时,所选的TreeNode保留其SystemColors.Highlight背景色?因为即使HideSelection设置为false,选定的BackColor也很难看到。

当TreeView获得焦点时选择的TreeNode:

Focused

当TreeView失去焦点时选择的TreeNode:

Unfocused

提前致谢。

编辑:我知道可以将DrawMode设置为OwnerDrawAll,然后添加自定义的DrawNode事件。我之前尝试过这个方法,但问题是我不知道如何正确绘制TreeNode的对应ImageKey。

8个回答

20
如果你只想保留 SystemColors.Highlight 背景色,那么你不需要将 TreeViewDrawMode 属性设置为 TreeViewDrawMode.OwnerDrawAll。将其设置为TreeViewDrawMode.OwnerDrawText 应该就足够了,这样你就不必担心绘制 TreeNode 对应的 ImageKey
  1. TreeView.DrawMode 设置为 TreeViewDrawMode.OwnerDrawText

    treeView.DrawMode = TreeViewDrawMode.OwnerDrawText;
    
  2. Treview.HideSelection设置为false,这样节点状态就会保持选中:

  3. treeView.HideSelection= false;
    
  4. 添加DrawNode事件处理程序,使用SystemColors.Highlight颜色绘制背景:

  5. 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;
      }
    }
    

我刚在阅读你的回答前2分钟弄明白了如何做,本来想接受你的回答,所以我先试了试,但是它没起作用。解决方案可以在上面找到。 - Spark
哎呀,你说得对。我忘记添加关于“HideSelection”属性的部分了。我已经更新了答案... - IronGeek
4
如果FullRowSelect为false,那么这只能正常工作,否则在选择时会出现奇怪的闪烁,并且仅保留文本的背景而不是整行。很遗憾。 - Spark
@Spark:我没有看到任何闪烁。但是如果使用复选框,复选框的右边框会被文本隐藏。(.net 4.8,Win 10 x64)。并且如果使用FullRowSelect(不仅启用,而且还要使用:只有在TreeLines关闭时才会使用),则文本为蓝色,如所需,但行的其余部分为浅灰色。 - Tobias Knauss

15

这个解决方案非常有效:

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

1
FYI,当以编程方式设置所选项目时,此操作会失败(解决方案:手动调用事件)。 - Christian
5
请注意,这仅在HideSelection为True(默认值)时起作用! - Anders Lindén
这对我完全没有用 - 节点仍然几乎看不见...下面的答案(处理drawnode)似乎完美地解决了问题。 - DanW

10

我使用这个解决方案,它的效果更好

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

编辑

根据这份文档,

默认值为Color.Empty

因此,在treeView1_Enter中最好像这样设置颜色:

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

以前的回答

treeView1.SelectedNode.BackColor = treeView1.BackColor;
treeView1.SelectedNode.ForeColor = treeView1.ForeColor;

非常好的答案,谢谢。请注意,这仅在HideSelection为True(默认值)时有效。 - Darrel K.
1
非常好的答案,谢谢!但是,如果在树形视图失去焦点时通过代码更改所选节点,则它将无法工作。这可以通过使用BeforeSelect和AfterSelect事件来解决。 - Joh
如果与@Joh的建议相结合,这是最简单和最有效的解决方案。 - Tobias Knauss

3

如果您只需要在树视图失去焦点时突出显示所选节点,则只需使用treeView.HideSelection = false;

实际上,默认情况下选择的节点是突出显示的,但不可见,因此我们只需要将HideSelection属性设置为false。


1
问题如问题所述,即使HideSelection = false,当树形视图没有焦点时,所选节点与其他节点几乎无法区分。 - Joh

1

我在这里(MSDN)找到了一个很好的解决方案。

对我非常有效,希望能帮助其他人。

  1. 添加一个新类(名为MyTreeView.cs的文件)到您的项目中,并粘贴下面显示的代码。
  2. 编译。
  3. 从工具箱的顶部将名为“MyTreeView”的新控件拖放到您的表单上。

如果您知道自己在做什么,也可以进行自定义。

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

0
以下代码即使在程序选择节点的情况下也能正常工作。
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;
}

这似乎不起作用,如果树没有被选择,这也是问题的重点所在。 - Mick Bruno

0

IronGeeks的这个变体对我有用,并且与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

主要区别在于即使 TreeView 获得焦点,自定义绘制也会执行。而且使用的是 DrawString() 而不是 DrawText()

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接