这是一个从Control 派生的自定义控件,可以做成半透明的。
界面是一个彩色圆圈,可以包含几个数字。
控件公开了这些自定义属性:
Opacity:控件的不透明度级别BackGround[0, 255]
InnerPadding:内部矩形之间的距离,它定义了圆形边界和控件边界。
FontPadding:文本和内部矩形之间的距离。
通过覆盖CreateParams 获得透明度,然后设置ExStyle |= WS_EX_TRANSPARENT;
Control.SetStyle()方法用于修改控制行为,添加这些ControlStyles:
▶ ControlStyles.Opaque:防止绘制控件的背景,因此它不受系统管理。结合 CreateParams 设置控件的扩展样式为WS_EX_TRANSPARENT,控件变得完全透明。
▶ ControlStyles.SupportsTransparentBackColor 控件接受 Alpha 值,因为它是 BackGround 颜色。如果不设置ControlStyles.UserPaint,它将不会用于模拟透明度。我们正在通过其他方式自己做到这一点。
要查看它的工作情况,请创建一个新的 Class 文件,用此代码替换其中的所有代码保留名称空间并构建项目/解决方案。
新的自定义控件将出现在工具箱中。将其放在表单上。根据需要修改其自定义属性。
控件的可视化表示:
注意和免责声明:
- 这是一个原型控件,缺少自定义设计器(此处无法发布,代码太多,还连接到框架)。
如此处所述,它可用于完全重叠表单或其他容器中的其他控件。在这个简化的实现中不处理部分重叠。
- 字体被硬编码为
Segoe UI,因为此字体具有简化圆形区域中间文本位置的基线。
其他字体有不同的基线,需要更复杂的处理。
请参阅:TextBox with dotted lines for typing 了解基础数学。
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")]
class RoundCenterControl : Control, INotifyPropertyChanged, ISupportInitialize
{
private const int WS_EX_TRANSPARENT = 0x00000020;
private readonly int internalFontPadding = 10;
private bool initializing = false;
private Font m_CustomFont = null;
private Color m_BackGroundColor;
private int m_InnerPadding = 0;
private int m_FontPadding = 20;
private int m_Opacity = 128;
public event PropertyChangedEventHandler PropertyChanged;
public RoundCenterControl() => InitializeComponent();
private void InitializeComponent()
{
SetStyle(ControlStyles.Opaque |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
BackColor = Color.LimeGreen;
ForeColor = Color.White;
}
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
}
public new Font Font
{
get => m_CustomFont;
set {
m_CustomFont = value;
if (initializing) return;
FontAdapter(m_CustomFont, DeviceDpi);
NotifyPropertyChanged();
}
}
public override string Text {
get => base.Text;
set { base.Text = value;
NotifyPropertyChanged();
}
}
public int InnerPadding {
get => m_InnerPadding;
set {
m_InnerPadding = value;
if (initializing) return;
m_InnerPadding = (int)ValidateRange(value, 0, ClientRectangle.Height - internalFontPadding);
NotifyPropertyChanged();
}
}
public int FontPadding {
get => m_FontPadding;
set {
m_FontPadding = value;
if (initializing) return;
m_FontPadding = (int)ValidateRange(value, internalFontPadding, ClientRectangle.Height - internalFontPadding);
FontAdapter(m_CustomFont, DeviceDpi);
NotifyPropertyChanged();
}
}
public int Opacity {
get => m_Opacity;
set { m_Opacity = (int)ValidateRange(value, 0, 255);
UpdateBackColor(m_BackGroundColor);
NotifyPropertyChanged();
}
}
public override Color BackColor {
get => this.m_BackGroundColor;
set {
UpdateBackColor(value);
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = null)
{
Parent?.Invalidate(Bounds, true);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
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);
};
};
}
private RectangleF InnerRectangle()
{
var minMax = GetMinMax(ClientRectangle.Height, ClientRectangle.Width);
var size = new SizeF(minMax.min - m_InnerPadding,
minMax.min - m_InnerPadding);
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 size = (rect.Height - m_FontPadding) * (72.0f / Dpi) - internalFontPadding;
int fontSize = (int)ValidateRange(size, 6, size);
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 float ValidateRange(float Value, float Min, float Max)
=> Math.Max(Math.Min(Value, Max), Min); //(Value < Min) ? Min : ((Value > Max) ? Max : Value);
private (float min, float max) GetMinMax(float value1, float value2)
=> (Math.Min(value1, value2), Math.Max(value1, value2));
public void BeginInit() => initializing = true;
public void EndInit()
{
initializing = false;
Font = new Font("Segoe UI", 50, FontStyle.Regular, GraphicsUnit.Pixel);
FontPadding = m_FontPadding;
InnerPadding = m_InnerPadding;
}
}