Winforms ToolStrip.BackColor 返回错误的颜色

chu*_*ill 1 c# colors toolstrip winforms

我的问题的根源

我使用 winforms 在 C# 中创建了一个非常简单的虚拟应用程序。它仅包含一个 ToolStrip 和该 ToolStrip 内的一个 TrackBar。TrackBar 应具有与 ToolStrip 相同的 BackColor,但 ToolStrip.BackColor 属性似乎不返回实际的背景颜色。

public Form1()
{
    //// Here the ToolStrip is created.
    //// This code is generated by the designer.
    this.InitializeComponent();
    //// Here the the TrackBar is created.
    //// I think this is impossible to do via the Designer.
    this.CreateSpeedSlider();
}

private void CreateSpeedSlider()
{
    this.toolStrip.SuspendLayout();
    this.speedSlider = new TrackBar
    {
        TickStyle = TickStyle.None,
        AutoSize = false,
        Height = this.toolStrip.Height,
        Minimum = 0,
        Maximum = 10,
        BackColor = this.toolStrip.BackColor,
    };

    this.toolStrip.Items.Add(new ToolStripControlHost(this.speedSlider));
    this.toolStrip.ResumeLayout();
}
Run Code Online (Sandbox Code Playgroud)

结果是浅灰色 ToolStrip 上的白色 TrackBar。所以看来

this.toolStrip.BackColor
Run Code Online (Sandbox Code Playgroud)

不返回实际用于绘制它的 ToolStrip 的背景颜色。

然而...

如果我将我的功能更改为

private void CreateSpeedSlider()
{
    this.toolStrip.BackColor = this.toolStrip.BackColor;
    ...
}
Run Code Online (Sandbox Code Playgroud)

我在白色工具条上看到一个白色滑块。所以...将属性的值分配给自身实际上会将颜色从浅灰色更改为白色???

所以问题是...

有没有什么好方法可以在没有这种“黑客”的情况下检索 ToolStrip 的正确背景颜色?

这种行为是故意的还是我错过了什么?

def*_*ale 5

BackColor 是一个环境属性

Control.BackColor是一个环境属性:

BackColor 属性是一个环境属性。环境属性是一种控件属性,如果未设置,则从父控件中检索该属性。例如,默认情况下,按钮将与其父窗体具有相同的背景颜色。有关环境属性的更多信息,请参阅 AmbientProperties 类或 Control 类概述。

这意味着它是这样实现的(来源):

public virtual Color BackColor
{
     get
     {
          Color c = RawBackColor; // inheritedProperties.BackColor
          if (!c.IsEmpty)
          {
               return c;
          }
          Control p = ParentInternal;
          if (p != null && p.CanAccessProperties)
          {
               c = p.BackColor;
               if (IsValidBackColor(c))
               {
                    return c;
               }
          }
          ...
Run Code Online (Sandbox Code Playgroud)

有一个内部属性RawBackColor,它是为此特定控件显式设置的颜色。BackColor本身是计算的 - 如果没有为此控件显式设置颜色,它将尝试返回BackColor其父级的颜色。

ToolStrip 的呈现方式有所不同。

要实现菜单的不同样式,ToolStrip支持可配置渲染

ToolStrip 类实现的呈现方案与其他 Windows 窗体控件显着不同。通过此方案,您可以轻松应用样式和主题。

它的意思是,ToolStrip有一个Renderer并且渲染器的初始化取决于系统设置。因此,一般来说,没有简单的方法来获取工具条的背景颜色。

ToolstripProfessionalRenderer默认情况下使用的可能性很大。这种类型的渲染器实际上用渐变来绘制背景。它有一个ColorTable 属性,该属性又定义MenuStripGradientBeginMenuStripGradientEnd

因此,在大多数(但不是全部)情况下,您可以通过以下方式确定工具提示的默认背景颜色:

var renderer = (ToolStripProfessionalRenderer)toolStrip.Renderer;
Console.WriteLine(renderer.ColorTable.MenuStripGradientBegin);
Console.WriteLine(renderer.ColorTable.MenuStripGradientEnd);
Run Code Online (Sandbox Code Playgroud)

如果您尝试托管具有均匀绘制背景的控件(例如 )TrackBar,它可能会在渐变填充的工具条中突出。

ToolStripRenderer 考虑明确设置的颜色

我在任何地方都找不到这种行为的记录,但根据ToolStripRenderer来源RawBackColor,只有在未设置工具条的情况下才会绘制背景(填充渐变) :

internal bool ShouldPaintBackground(Control control)
{
    return (control.RawBackColor == Color.Empty && control.BackgroundImage == null);
}
Run Code Online (Sandbox Code Playgroud)

因此,如果您明确设置ToolStrip.BackColor为任何颜色,则渲染器将仅使用该颜色作为背景色。这种令人困惑的行为解释了原因:

toolStrip.BackColor = toolStrip.BackColor;
Run Code Online (Sandbox Code Playgroud)

有实际效果。

现在做什么?

我可以看到多种前进的道路,但没有一个是好的:

  1. 将工具条和轨迹栏设置BackColor为某种已知颜色。这会破坏工具条背景中美丽的渐变效果。如果您不太关心渐变,这是最简单的方法。

  2. 使托管控件透明,以便工具条的背景显示出来。由于某种原因,这不起作用TrackBar,因为它不支持透明背景。请参阅此问题,了解可能的选项(我没有尝试其中任何一个):Trackbar Background in a TabControl

  3. 覆盖轨迹栏OnPaint并用渐变填充它,以便它与工具条的背景融为一体。这听起来有问题,但可行。