为什么WinForms标签不想透明?

29

为什么我不能将标签(Label)的BackColor设置为透明?我以前做过,但现在它就是不想要...

我创建了一个新的用户控件(UserControl),向其中添加了一个进度条(ProgressBar)和一个标签(Label)。当我将标签的BackColor设置为透明时,它仍然是灰色的 =/ 为什么会这样呢?

我的目的是让标签位于进度条的顶部,以便其文本显示在进度条内部...


请参考亚历山大·威廉斯的回答。 - blez
关于您最后一句话(将文本放入进度条):Microsoft的进度条指南说:不要在进度条上放置完成百分比或任何其他文本。这样的文本无法访问,也不兼容使用主题。 因此,如果您能找到另一种方法,它将更兼容,看起来更好,更一致。请参阅上面链接的指南。 - miroxlav
11个回答

32

在你的项目中添加一个新类,并贴上下面展示的代码。构建项目,然后从工具箱的顶部将新控件拖放到你的表单上。

using System;
using System.Windows.Forms;

public class TransparentLabel : Label {
  public TransparentLabel() {
    this.SetStyle(ControlStyles.Opaque, true);
    this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
  }
  protected override CreateParams CreateParams {
    get {
      CreateParams parms = base.CreateParams;
      parms.ExStyle |= 0x20;  // Turn on WS_EX_TRANSPARENT
      return parms;
    }
  }
}

1
嗯,这似乎几乎可以工作。问题是当它在进度条上方时。有时候文本似乎会消失和重新出现... - Svish
不支持堆叠效果,只能跟踪容器的更改。 - Hans Passant
那么,如果我改变了用户控件的背景颜色呢?(它们之间没有进度条?) - Svish
没错。或者容器上绘制的任何其他内容。 - Hans Passant
我通过艰难的方式发现,如果你省略了Hans放置的构造函数,它似乎可以工作,直到你将Enabled设置为false,此时透明度就会丢失。 - Brian

14

WinForms并不真正支持透明控件,但是您可以自己创建透明控件。 在这里看看我的答案

在您的情况下,您应该子类化进度条并重写OnPaint方法,在进度条上绘制文本。


你会如何在进度条上绘制文本? - Svish
我没有为进度条做过这个,但是你可以为OnPaint方法创建一个重载。在这里,你首先调用base.OnPaint,然后使用传递给事件参数的图形对象在控件顶部绘制文本。 - Rune Grimstad
4
不确定为什么一个回答只是引用了另一个SO的回答,并建议“在谷歌上搜索”,而没有给出方法的重要细节,却被接受了,而完整的解决方案已经在这个页面的另一个回答中公布了。 - jwg

9
最简单的解决方案如下:
  1. 在可视化编辑器或您的表单构造函数中将背景颜色设置为透明:

    this.label1.BackColor = System.Drawing.Color.Transparent;

  2. 将标签的 Parent 属性设置为您想要在文本后面显示的控件。这可以在表单构造函数或 Load 方法中完成:

    this.label1.Parent = progressBar1;

确实,这并不像 DirectX 中的真正透明。您在显示器上看到的结果仅由两个图层组成。使用此方法,您不能累加超过两个具有各自定义的透明度(由 alpha 参数定义)的图层,但它非常适用于 Winforms 编程中的许多实际情况。

5
请使用LinkLabel而不是普通的Label。
    private void MakeTransparentLabel(System.Windows.Forms.LinkLabel LinkLabel)
    {
        this.MakeTransparentLabel(LinkLabel, Color.White);
    }
    private void MakeTransparentLabel(System.Windows.Forms.LinkLabel LinkLabel, Color ForeColor)
    {
        LinkLabel.ForeColor = ForeColor;
        LinkLabel.LinkColor = ForeColor;
        LinkLabel.VisitedLinkColor = ForeColor;
        LinkLabel.ActiveLinkColor = ForeColor;
        LinkLabel.DisabledLinkColor = ForeColor;
        LinkLabel.LinkArea = new LinkArea(0, 0);
        LinkLabel.LinkBehavior = LinkBehavior.NeverUnderline;
        LinkLabel.Cursor = Cursors.Arrow;
        LinkLabel.BackColor = Color.Transparent;
    }
    private void SetTransparentLabelText(System.Windows.Forms.LinkLabel LinkLabel, string Text)
    {
        if (string.IsNullOrEmpty(Text)) { LinkLabel.Text = " "; return; }
        LinkLabel.Text = Text;
    }

3

这是一种非常简单的解决方案,非常实用:

public class MyLabel : Label
{
    private bool fTransparent = false;
    public bool Transparent
    {
        get { return fTransparent; }
        set { fTransparent = value; }
    }
    public MyLabel() : base()
    {
    }
    protected override CreateParams CreateParams
    {
        get
        {
            if (fTransparent)
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
                return cp;
            }
            else return base.CreateParams;
        }
    }
    protected override void WndProc(ref Message m)
    {
        if (fTransparent)
        {
            if (m.Msg != 0x14 /*WM_ERASEBKGND*/ && m.Msg != 0x0F /*WM_PAINT*/)
                base.WndProc(ref m);
            else 
            {
                if (m.Msg == 0x0F) // WM_PAINT
                    base.OnPaint(new PaintEventArgs(Graphics.FromHwnd(Handle), ClientRectangle));
                DefWndProc(ref m);
            }
        }
        else base.WndProc(ref m);
    }
}

当标签的背景颜色为透明时,标签仅在创建时获取其底层控件的图像,之后标签背景颜色保持不变。每次标签重绘时,它都会重绘到固定的颜色或图案。
重写CreateParams会影响控件窗口的创建方式,这可以实现真正的透明度。
通过重写WndProc,您可以控制应将哪些消息传递给基类。我们必须过滤WM_ERASEBKGND和WM_PAINT消息,但我们还必须触发绘制事件。

这个能在移动进度条上工作吗? - Svish
它应该能够正常工作,因为这是告诉本地底层控件不要自行绘制的方法。 - Elvedin Hamzagic

1

这是我一段时间前编写的一个透明控件,它可以显示旋转的文本。大部分代码来自这里,尽管我记得我不得不进行一些调整才能使其正常工作。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Windows.Forms;

namespace MyNamespace
{
    public partial class RotatedText : UserControl
    {
        private readonly Timer _invalidationTimer;
        private const int WS_EX_TRANSPARENT = 0x00000020;

        public RotatedText()
        {
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            InitializeComponent();

            _invalidationTimer = new Timer {Interval = 500, Enabled = true};
            _invalidationTimer.Tick += TickHandler;
        }

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Category("Appearance")]
        [Description("Text which appears in control")]
        public string Text { get; set; }

        #region Transparent background
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= WS_EX_TRANSPARENT;
                return cp;
            }
        }

        private void TickHandler(object sender, EventArgs e)
        {
            InvalidateEx();
        }

        private void InvalidateEx()
        {
            if (Parent != null)
                Parent.Invalidate(Bounds, false);
            else
                Invalidate();
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //Intentionally do nothing - stops background from drawing
            //base.OnPaintBackground(e);
        } 
        #endregion

        //Rotate text and draw
        protected override void OnPaint(PaintEventArgs e)
        {
            double angleRadians = Math.Atan2(Height, Width);
            float angleDegrees = -1*(float) (angleRadians*180/Math.PI);
            angleDegrees *= 0.9f;
            e.Graphics.RotateTransform(angleDegrees, MatrixOrder.Append);
            e.Graphics.TranslateTransform(20, Height - 75, MatrixOrder.Append);
            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
            Font font = new Font("Ariel", 50);
            e.Graphics.DrawString(Text, font, Brushes.Gray, 1, 2); //Shadow
            e.Graphics.DrawString(Text, font, Brushes.Red, 0, 0);
        }
    }
}

1

如果你想专注于设计Windows应用程序,我建议你使用WPF。

在WPF中使控件透明非常容易。

<TextBox Width="200" Height="40" Opacity="0.5"/>

6
我确定 WPF 能正确处理透明标签,但是只是想指出这个例子中的代码,如果应用于标签,会使文本也变成半透明 - 这不是所期望的结果! - Roman Starkov

0

你想要实现的完全可以做到。只需要花点时间来调整控件。可以创建一个透明背景的 Label 控件,然后将其放置在 Progressbar 控件上方。

请查看我的回答另一个 SO 问题。


0
关于您的问题解释,Windows 不会像您期望的那样为背景控件提供透明度 - 我猜测灰色背景实际上是窗体表面。在窗体表面和标签之间绘制的任何控件都将被忽略。

0

正如我之前回答中的评论所述,控件是默认行为,并且是我记得的透明。

无论如何,您是否尝试设置UserControl的背景属性或包含标签的容器(Panel、Form等)?您的标签应该反映该颜色 :)


旧回答: 我已经有一段时间没有做winforms编程了,但是据我回忆,标签默认是透明的?因此只有文本获得实际颜色,背景颜色模仿其后面的任何内容 :)


1
不,标签默认具有控件的背景。但是,您是正确的,将其设置为透明会模仿托管它的控件的颜色,因此它会将背景绘制为纯灰色。 - Matt Hamilton
应该启动WinForms并检查:P,真正的控件是默认行为,只是因为它们共享相同的颜色,所以看起来透明:) - thmsn

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