带文本的半透明圆形控件

2024-01-13

我正在开发一个项目,其中我需要添加一个圆形形状的控件,中间有一些文本。
我的问题是圆圈太小,当我调整它的大小时,它与其他控件重叠。我想画一个与正方形宽度相同的圆。
否则。如何使控件的背景透明?

我正在使用下面的代码:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    using (Bitmap bitmap = new Bitmap(this.Width, this.Height))
    {
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.Clear(this.BackColor);

            using (SolidBrush brush = new SolidBrush(this._FillColor))
            {                      
                graphics.FillEllipse(brush, 0x18 - 6, 0x18 - 6, (this.Width - 0x30) + 12, (this.Height - 0x30) + 12);
            }

            Brush FontColor = new SolidBrush(this.ForeColor);
            SizeF MS = graphics.MeasureString(Convert.ToString(Convert.ToInt32((100 / _Maximum) * _Value)), Font);
            graphics.DrawString(Convert.ToString(Convert.ToInt32((100 / _Maximum) * _Value)), Font, FontColor, Convert.ToInt32((Width / 2 - MS.Width / 2) + 2), Convert.ToInt32((Height / 2 - MS.Height / 2) + 3));
            bitmap.MakeTransparent(this.BackColor);
            e.Graphics.DrawImage(bitmap, 0, 0);

            graphics.Dispose();
            bitmap.Dispose();
        }
    }
}

这是一个自定义控件,源自Control,可以做成半透明的。
该界面是一个彩色圆圈,其中可以包含几个数字。

该控件公开这些自定义属性:

Opacity:控件的不透明度级别BackGround [0, 255]
InnerPadding:内部矩形之间的距离,定义圆边界和控件边界。
FontPadding:文本与内部矩形之间的距离。

透明度是压倒一切的创建参数 https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.createparams,然后设置ExStyle |= WS_EX_TRANSPARENT;

The Control.SetStyle() https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.setstyle方法用于修改控件行为,添加这些控件样式 https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.controlstyles:

ControlStyles.Opaque:防止绘制控件的背景,因此它不受系统管理。结合CreateParams设置控件的扩展样式为WS_EX_TRANSPARENT,控件变得完全透明。

ControlStyles.SupportsTransparentBackColor该控件接受 Alpha 值BackGround颜色。无需同时设置ControlStyles.UserPaint它不会用于模拟透明度。我们正在通过其他方式自己做到这一点。


要查看它的工作情况,请创建一个新的类文件,用此代码替换里面的所有代码保留命名空间并构建项目/解决方案。
新的自定义控件将出现在工具箱中。将其放在表格上。根据需要修改其自定义属性。

控件的直观表示:

注意事项和免责声明:

  • 这是一个原型控件,缺少自定义设计器(无法在这里发布,代码太多,还连接到框架)。
    正如这里所介绍的,它可用于完全地与表单或其他容器中的其他控件重叠。在这个简化的实现中不处理部分重叠。
  • 字体被硬编码为Segoe UI,因为此字体有一个基线,可以简化圆形区域中间文本的位置。
    其他字体有不同的基线,这需要更复杂的处理。
    See: 带虚线的文本框用于输入 https://stackoverflow.com/a/64673192/7444103用于基础数学。

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

[DesignerCategory("Code")]
public class RoundCenterLabel : Label, INotifyPropertyChanged, ISupportInitialize 
{
    private const int WS_EX_TRANSPARENT = 0x00000020;
    private bool IsInitializing = false;
    private Point MouseDownLocation = Point.Empty;
    private readonly int fontPadding = 4;
    private Font m_CustomFont = null;
    private Color m_BackGroundColor;
    private int m_InnerPadding = 0;
    private int m_FontPadding = 25;
    private int m_Opacity = 128;

    public event PropertyChangedEventHandler PropertyChanged;

    public RoundCenterLabel() => InitializeComponent();

    private void InitializeComponent()
    {
        SetStyle(ControlStyles.Opaque |
                 ControlStyles.SupportsTransparentBackColor |
                 ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
        m_CustomFont = new Font("Segoe UI", 50, FontStyle.Regular, GraphicsUnit.Pixel);
        BackColor = Color.LimeGreen;
        ForeColor = Color.White;
    }

    protected override CreateParams CreateParams {
        get {
            var cp = base.CreateParams;
            cp.ExStyle |= WS_EX_TRANSPARENT;
            return cp;
        }
    }

    public new Font Font
    {
        get => m_CustomFont;
        set { 
            m_CustomFont = value;
            if (IsInitializing) return;
            FontAdapter(value, DeviceDpi);
            NotifyPropertyChanged();
        }
    }

    public override string Text {
        get => base.Text;
        set { base.Text = value;
              NotifyPropertyChanged();
        }
    }

    public int InnerPadding {
        get => m_InnerPadding;
        set {
            if (IsInitializing) return;
            m_InnerPadding = ValidateRange(value, 0, ClientRectangle.Height - 10);
            NotifyPropertyChanged(); }
    }

    public int FontPadding {
        get => m_FontPadding;
        set {
            if (IsInitializing) return;
            m_FontPadding = ValidateRange(value, 0, ClientRectangle.Height - 10);
            NotifyPropertyChanged();
        }
    }

    public int Opacity {
        get => m_Opacity;
        set { m_Opacity = ValidateRange(value, 0, 255);
              UpdateBackColor(m_BackGroundColor);
              NotifyPropertyChanged();
        }
    }

    public override Color BackColor {
        get => m_BackGroundColor;
        set { UpdateBackColor(value);
              NotifyPropertyChanged();
        }
    }

    protected override void OnLayout(LayoutEventArgs e)
    {
        base.OnLayout(e);
        base.AutoSize = false;
    }

    private void NotifyPropertyChanged([CallerMemberName] string PropertyName = null)
    {
        InvalidateParent();
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        MouseDownLocation = e.Location;
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        if (e.Button == MouseButtons.Left) {
            var loc = new Point(Left + (e.X - MouseDownLocation.X), Top + (e.Y - MouseDownLocation.Y));
            InvalidateParent();
            BeginInvoke(new Action(() => Location = loc));
        }
    }

    private void InvalidateParent()
    {
        Parent?.Invalidate(Bounds, true);
        Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (var format = new StringFormat(StringFormatFlags.LineLimit | StringFormatFlags.NoWrap, CultureInfo.CurrentUICulture.LCID))
        {
            format.LineAlignment = StringAlignment.Center;
            format.Alignment = StringAlignment.Center;
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

            using (var circleBrush = new SolidBrush(m_BackGroundColor))
            using (var foreBrush = new SolidBrush(ForeColor))
            {
                FontAdapter(m_CustomFont, e.Graphics.DpiY);
                RectangleF rect = InnerRectangle();
                e.Graphics.FillEllipse(circleBrush, rect);
                e.Graphics.DrawString(Text, m_CustomFont, foreBrush, rect, format);
            };
        };
    }

    public void BeginInit() => IsInitializing = true;

    public void EndInit()
    {
        IsInitializing = false;
        Font = new Font("Segoe UI", 50, FontStyle.Regular, GraphicsUnit.Pixel);
        FontPadding = m_FontPadding;
        InnerPadding = m_InnerPadding;
    }

    private RectangleF InnerRectangle()
    {
        (float Min, _) = GetMinMax(ClientRectangle.Height, ClientRectangle.Width);
        var size = new SizeF(Min - (m_InnerPadding / 2), Min - (m_InnerPadding / 2));
        var position = new PointF((ClientRectangle.Width - size.Width) / 2,
                                  (ClientRectangle.Height - size.Height) / 2);
        return new RectangleF(position, size);
    }

    private void FontAdapter(Font font, float dpi)
    {
        RectangleF rect = InnerRectangle();
        float fontSize = ValidateRange(
            (int)(rect.Height - m_FontPadding), 6, 
            (int)(rect.Height - m_FontPadding)) / (dpi / 72.0F) - fontPadding;

        m_CustomFont.Dispose();
        m_CustomFont = new Font(font.FontFamily, fontSize, font.Style, GraphicsUnit.Pixel);
    }

    private void UpdateBackColor(Color color)
    {
        m_BackGroundColor = Color.FromArgb(m_Opacity, Color.FromArgb(color.R, color.G, color.B));
        base.BackColor = m_BackGroundColor;
    }

    private int ValidateRange(int Value, int Min, int Max) 
        => Math.Max(Math.Min(Value, Max), Min); // (Value < Min) ? Min : ((Value > Max) ? Max : Value);

    private (float, float) GetMinMax(float Value1, float Value2) 
        => (Math.Min(Value1, Value2), Math.Max(Value1, Value2));
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带文本的半透明圆形控件 的相关文章

随机推荐