I am working on a project wherein I need to add a Circle with text in the middle. I am using the code below. But my problem is the circle is too small, when I resize it, it overlaps other control. I want to draw the circle same width as the square or how will make the back ground as transparent?
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();
}
}
}
This is a Custom Control derived from a standard Label
, which can be made translucent.
The interface is a colored circle which can contain a couple of numbers.
The Control exposes these custom properties:
Opacity
: The level of opacity of the control BackGround
[0, 255]
InnerPadding
: The distance between the inner rectangle, which defines the circle bounds and the control bounds.
FontPadding
: The distance between the Text and the Inner rectangle.
Transparency is obtained overriding CreateParams, then setting ExStyle |= WS_EX_TRANSPARENT;
The Control.SetStyle() method is used to modify the control behaviour, adding these ControlStyles:
ControlStyles.Opaque
prevents the painting of the control BackGround
, so it's not managed by the System.
ControlStyles.SupportsTransparentBackColor
the control accepts Alpha values for it's BackGround
color.
To see it at work, create a new Class file, substitute all the code inside with this code
preserving the NameSpace and build the Project/Solution.
The new Custom Control will appear in the ToolBox.
Drop it on a Form. Modify its custom properties as needed.
A visual representation of the control:
Apparently, ScreenToGif
ignores the Pixel change on full opacity.
In its opinion nothing changes, so it optimizes
it showing nothing.
Note:
I didn't use TextRenderer here because of its padding. It's harder to control in this context: the vertical center position needs to be adjusted and it doesn't provide any quality enhancements.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Globalization;
using System.Windows.Forms;
[DesignerCategory("Code")]
class RoundCenterLabel : Label, INotifyPropertyChanged
{
internal const int WS_EX_TRANSPARENT = 0x00000020;
internal Font m_CustomFont = null;
internal Color m_BackGroundColor;
internal int m_InnerPadding = 0;
internal int m_FontPadding = 25;
internal int m_Opacity = 128;
public event PropertyChangedEventHandler PropertyChanged;
public RoundCenterLabel() => InitializeComponent();
private void InitializeComponent()
{
this.SetStyle(ControlStyles.Opaque |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.m_CustomFont = new Font("Segoe UI", 50, FontStyle.Regular, GraphicsUnit.Pixel);
this.BackColor = Color.LimeGreen;
this.ForeColor = Color.White;
}
private void NotifyPropertyChanged(string PropertyName)
{
this.Invalidate();
this.FindForm()?.Refresh();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
public new Font Font
{
get => this.m_CustomFont;
set { this.m_CustomFont = value;
FontAdapter(value, this.DeviceDpi);
NotifyPropertyChanged(nameof(this.Font));
}
}
public override string Text {
get => base.Text;
set { base.Text = value;
NotifyPropertyChanged(nameof(this.Text));
}
}
public int InnerPadding {
get => this.m_InnerPadding;
set { this.m_InnerPadding = CheckValue(value, 0, this.ClientRectangle.Height - 10);
NotifyPropertyChanged(nameof(this.InnerPadding)); }
}
public int FontPadding {
get => this.m_FontPadding;
set { this.m_FontPadding = CheckValue(value, 0, this.ClientRectangle.Height - 10);
NotifyPropertyChanged(nameof(this.FontPadding));
}
}
public int Opacity {
get => this.m_Opacity;
set { this.m_Opacity = CheckValue(value, 0, 255);
UpdateBackColor(this.m_BackGroundColor);
NotifyPropertyChanged(nameof(this.Opacity));
}
}
public override Color BackColor {
get => this.m_BackGroundColor;
set { UpdateBackColor(value);
NotifyPropertyChanged(nameof(this.BackColor));
}
}
protected override void OnLayout(LayoutEventArgs evt)
{
base.OnLayout(evt);
base.AutoSize = false;
}
protected override void OnPaint(PaintEventArgs e)
{
StringFormat format = new StringFormat(StringFormatFlags.LineLimit | StringFormatFlags.NoWrap, CultureInfo.CurrentUICulture.LCID) {
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
using (SolidBrush CircleBrush = new SolidBrush(this.m_BackGroundColor))
using (SolidBrush ForeBrush = new SolidBrush(this.ForeColor))
{
this.FontAdapter(this.m_CustomFont, e.Graphics.DpiY);
RectangleF rect = InnerRectangle();
e.Graphics.FillEllipse(CircleBrush, rect);
e.Graphics.DrawString(this.Text, this.m_CustomFont, ForeBrush, rect, format);
}
}
private RectangleF InnerRectangle()
{
Tuple<decimal, decimal> refSize = GetMinMax(this.ClientRectangle.Height, this.ClientRectangle.Width);
SizeF size = new SizeF((float)refSize.Item1 - (this.m_InnerPadding / 2),
(float)refSize.Item1 - (this.m_InnerPadding / 2));
PointF position = new PointF((this.ClientRectangle.Width - size.Width) / 2,
(this.ClientRectangle.Height - size.Height) / 2);
return new RectangleF(position, size);
}
private void FontAdapter(Font font, float Dpi)
{
RectangleF rect = InnerRectangle();
float FontSize = CheckValue((int)(rect.Height - this.m_FontPadding), 6,
(int)(rect.Height - this.m_FontPadding)) / (Dpi / 72.0F);
using (Font customfont = new Font(font.FontFamily, FontSize, font.Style, GraphicsUnit.Pixel))
this.m_CustomFont = (Font)customfont.Clone();
}
private void UpdateBackColor(Color color)
{
this.m_BackGroundColor = Color.FromArgb(this.m_Opacity, Color.FromArgb(color.R, color.G, color.B));
base.BackColor = this.m_BackGroundColor;
}
private int CheckValue(int Value, int Min, int Max)
{
return (Value < Min) ? Min : ((Value > Max) ? Max : Value);
}
private Tuple<decimal, decimal> GetMinMax(ValueType Value1, ValueType Value2)
{
if ((Value1 is Enum) || (Value1.GetType().IsNested)) return null;
if ((Value2 is Enum) || (Value2.GetType().IsNested)) return null;
return new Tuple<decimal, decimal>(Math.Min(Convert.ToDecimal(Value1), Convert.ToDecimal(Value2)),
Math.Max(Convert.ToDecimal(Value1), Convert.ToDecimal(Value2)));
}
protected override CreateParams CreateParams
{
get
{
CreateParams parameters = base.CreateParams;
parameters.ExStyle |= WS_EX_TRANSPARENT;
return parameters;
}
}
}