C# WinForms:当TreeView没有焦点时,如何高亮显示TreeNode

34

我正在制作一个用于编辑游戏场景的界面。基本上,它由事件组成,这些事件具有嵌套的条件和动作。因此,我计划使用两个树形视图-一个用于选择事件,另一个用于选择事件内的条件/动作以进行编辑。

现在,问题来了,如果我选择一个事件(在左侧的树形视图中)然后尝试在右侧的树形视图中选择某个条件/动作,左侧的树形视图将停止显示蓝色的选择矩形。显然,这很不好,因为现在用户不知道他正在编辑哪个事件!

我找到的唯一保留当前选择信息的方法是使用SelectedImageIndex,但这只是一个小小的不同的图像。

有没有其他方法可以在没有焦点的情况下突出显示树节点?我知道我可以使用Graphics.DrawRectangle之类的东西,但我听说绘图应该在Paint事件中完成,而treeview没有Paint事件…… 所以我想如果我在失去焦点的事件上绘制它,然后将窗体拖出屏幕或其他什么事情,它会被“擦除”?

总之,请告诉我是否有想法(除了使用单独的图标表示选定和未选定的树节点)。


你发布的图片已经消失了,你有备份吗?我会在此期间删除链接。 - Neuron
6个回答

71

16
实际上,在Windows7上使用默认配色方案时,这并没有帮助——当树形视图失去焦点时,选择的节点看起来像是未被选中的,我无法区分它和其他节点的区别。 - oleksa

18

它仍然显示,但只是以浅灰色显示,这取决于您的屏幕和当前设置,可能近乎不可见!

覆盖OnDrawNode事件。因此,您需要创建一个新类(称之为“SpecialTreeView”),并从Microsoft的TreeView继承,如class SpecialTreeView:TreeView。然后添加以下事件覆盖:

protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
    TreeNodeStates treeState = e.State;
    Font treeFont = e.Node.NodeFont ?? e.Node.TreeView.Font;

    // Colors.
    Color foreColor = e.Node.ForeColor;
    string strDeselectedColor = @"#6B6E77", strSelectedColor = @"#94C7FC";
    Color selectedColor = System.Drawing.ColorTranslator.FromHtml(strSelectedColor);
    Color deselectedColor = System.Drawing.ColorTranslator.FromHtml(strDeselectedColor);

    // New brush.
    SolidBrush selectedTreeBrush = new SolidBrush(selectedColor);
    SolidBrush deselectedTreeBrush = new SolidBrush(deselectedColor);

    // Set default font color.
    if (foreColor == Color.Empty)
        foreColor = e.Node.TreeView.ForeColor;

    // Draw bounding box and fill.
    if (e.Node == e.Node.TreeView.SelectedNode)
    {
        // Use appropriate brush depending on if the tree has focus.
        if (this.Focused)
        {
            foreColor = SystemColors.HighlightText;
            e.Graphics.FillRectangle(selectedTreeBrush, e.Bounds);
            ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, SystemColors.Highlight);
            TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
                                         foreColor, TextFormatFlags.GlyphOverhangPadding);
        }
        else
        {
            foreColor = SystemColors.HighlightText;
            e.Graphics.FillRectangle(deselectedTreeBrush, e.Bounds);
            ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, SystemColors.Highlight);
            TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
                                         foreColor, TextFormatFlags.GlyphOverhangPadding);
        }
    }
    else
    {
        if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
        {
            e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
            TextRenderer.DrawText(e.Graphics, e.Node.Text, hotFont, e.Bounds,
                                         System.Drawing.Color.Black, TextFormatFlags.GlyphOverhangPadding);
        }
        else
        {
            e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
            TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
                                         foreColor, TextFormatFlags.GlyphOverhangPadding);
        }
    }
}

编译代码,您应该在设计器的工具箱中看到“SpecialTreeView”。使用相同的名称将您的TreeView替换为这个新的控件,唯一不同的是选择颜色。 当选中时,它将是selectedColor,当未选中时则是deselectedColor

希望这可以帮助您。


是的,它确实做到了,但仅当你没有设置HideSelection为false时才会出现这种情况,而默认情况下HideSelection为true(什么用呢?嗯,典型的微软业务)。无论如何,感谢您的建议,我没有注意到它有“DrawNode”事件,一直在寻找典型的Paint事件... - Istrebitel
呃……它……不起作用!我已经使用自定义的TreeView控件,因为基本的控件没有拖放功能……重写OnDragDrop、OnDragEnter等方法是可以的……但是重写这个方法就不行。它根本不执行,我已经设置了断点——什么也没有。可能出了什么问题? - Istrebitel
1
啊,是的,找到了。它是DrawMode,如果设置为Normal,事件就不会触发。 - Istrebitel
我建议有能力的人合并这两个答案。 - James

12

快速解决方案:

设置以下属性:

  • HideSelection = false;
  • DrawMode = TreeViewDrawMode.OwnerDrawText;

然后在 DrawNode 事件处理程序中仅需执行以下操作:

private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e) {
  e.DrawDefault = true;
}
在Windows 7上,这将恢复旧的渲染方式,包括在选择周围绘制虚线框(实际上看起来有点过时)。文本在焦点下为白色,在无焦点下为黑色。背景保持蓝色和可见。这个答案并不新,其他答案也包含这些步骤,但这是最少需要的步骤(至少在Windows 7上,没有测试其他操作系统)。

我喜欢它,除了两个问题:1)复选框图标在右侧被切断,2)勾选/取消勾选时速度明显变慢... - AndrewRalon

8

并不是完美的解决方案,但相当接近:

treeView.HideSelection = false;
treeView.DrawMode = TreeViewDrawMode.OwnerDrawText;
treeView.DrawNode += (o, e) =>
{
    if (!e.Node.TreeView.Focused && e.Node == e.Node.TreeView.SelectedNode)
    {
        Font treeFont = e.Node.NodeFont ?? e.Node.TreeView.Font;
        e.Graphics.FillRectangle(Brushes.Gray, e.Bounds);
        ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, SystemColors.HighlightText, SystemColors.Highlight);
        TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
    }
    else
        e.DrawDefault = true;
};
treeView.MouseDown += (o, e) =>
{
    TreeNode node = treeView.GetNodeAt(e.X, e.Y);
    if (node != null && node.Bounds.Contains(e.X, e.Y))
        treeView.SelectedNode = node;
};

1
谢谢,这很有帮助。虽然我把Brushes.Gray改成了SystemBrushes.Highlight。 - GeoffCoope
这很有帮助,但使用它时,背景颜色不再绘制。我该如何修复它? - slow

3
找到了一种更简单的方法:
1. 将 TreeView.HideSelection 设置为 True。 2. 声明一个私有字段 private TreeNode _lastSelectedNode = null;。 3. 在 TreeView.AfterSelect 回调中添加以下内容:
private TreeNode _lastSelectedNode = null;
private void treeViewBenutzerverwaltung_AfterSelect(object sender, TreeViewEventArgs e)
{
    // Select new node
    e.Node.BackColor = SystemColors.Highlight;
    e.Node.ForeColor = SystemColors.HighlightText;
    if (_lastSelectedNode != null)
    {
        // Deselect old node
        _lastSelectedNode.BackColor = SystemColors.Window;
        _lastSelectedNode.ForeColor = SystemColors.WindowText;
    }
    _lastSelectedNode = e.Node;
}

我在这里写,因为“建议的编辑队列已满”。最后一行“lastSelectedNode = e.Node;”应该改为“_lastSelectedNode = e.Node;”。 - Magnus

1
类似于之前的样式,但外观更接近于Win10标准:
treeView.HideSelection = false;
treeView.DrawMode = TreeViewDrawMode.OwnerDrawText;

treeView.DrawNode += (o, e) =>
{
    if (e.Node == e.Node.TreeView.SelectedNode)
    {
        Font font = e.Node.NodeFont ?? e.Node.TreeView.Font;
        Rectangle r = e.Bounds;
        r.Offset(0, 1);
        Brush brush = e.Node.TreeView.Focused ? SystemBrushes.Highlight : Brushes.Gray;
        e.Graphics.FillRectangle(brush, e.Bounds);
        TextRenderer.DrawText(e.Graphics, e.Node.Text, font, r, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
    }
    else
        e.DrawDefault = true;
};

treeView.MouseDown += (o, e) =>
{
    TreeNode node = treeView.GetNodeAt(e.Location);
    if (node != null && node.Bounds.Contains(e.Location)) treeView.SelectedNode = node;
};

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