Custom-drawn CheckBox and RadioButton

My take on a drop-in replacement for custom-drawn CheckBox and RadioButton controls:

internal class CheckBoxEx : CheckBox
{
private const float factor = 0.6f;
private const int check = 2;
public Color DisableFore = SystemColors.GrayText;
protected override void OnPaint( PaintEventArgs pevent )
{
base.OnPaint( pevent );
// To clear the background, it is necessary to use a non-transparent color and
// this type of controls usually are, so fetch the first parent's that is not
var backColor = BackColor;
while( backColor.A == 0 )
{
if( Parent == null )
{
break;
}
backColor = Parent.BackColor;
}
if( backColor.A == 0 )
{
// The back color is still transparent, so max the alpha component
backColor = Color.FromArgb( 255, backColor.R, backColor.G, backColor.B );
}
pevent.Graphics.Clear( backColor );
// Calculate the size of the box by using a large default character (since there could be no text
// in the control) and neither that ClientRectangle or pevent.ClipRectangle might have a valid value
var height = TextRenderer.MeasureText( pevent.Graphics, "X", Font ).Height;
var boxSide = (int)( height * factor );
var boxFrame = new Rectangle( 0, ( height - boxSide ) / 2, boxSide, boxSide );
var textFrame = new Rectangle( boxFrame.Right + 2, 0, ClientRectangle.Width - boxFrame.Right - 2, height );
if( Text.Length == 0 )
{
// The box cannot be aligned with non-existing text
boxFrame.Y = 0;
}
// Draw box
using var backBrush = new SolidBrush( BackColor );
using var foreBrush = new SolidBrush( ForeColor );
using var forePen = new Pen( ForeColor );
if( Enabled == false )
{
forePen.Color = foreBrush.Color = DisableFore;
}
pevent.Graphics.FillRectangle( backBrush, boxFrame );
pevent.Graphics.DrawRectangle( forePen, boxFrame );
if( Checked == true )
{
boxFrame.Offset( check, check );
boxFrame.Width -= check + 1;
boxFrame.Height -= check + 1;
pevent.Graphics.FillRectangle( foreBrush, boxFrame );
}
// Draw text
if( Text.Length != 0 )
{
using var sf = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center
};
pevent.Graphics.SmoothingMode = SmoothingMode.HighQuality;
TextRenderer.DrawText( pevent.Graphics,
Text,
Font,
textFrame,
foreBrush.Color,
TextFormatFlags.Top | TextFormatFlags.Left );
pevent.Graphics.SmoothingMode = SmoothingMode.None;
if( Focused == true )
{
textFrame.Inflate( -1, 0 );
ControlPaint.DrawFocusRectangle( pevent.Graphics, textFrame, foreBrush.Color, backBrush.Color );
}
}
pevent.Dispose();
}
}
internal class RadioButtonEx : RadioButton
{
private const float factor = 0.6f;
private const int check = 3;
public Color DisableFore = SystemColors.GrayText;
protected override void OnPaint( PaintEventArgs pevent )
{
base.OnPaint( pevent );
// To clear the background, it is necessary to use a non-transparent color and
// this type of controls usually are, so fetch the first parent's that is not
var backColor = BackColor;
while( backColor.A == 0 )
{
if( Parent == null )
{
break;
}
backColor = Parent.BackColor;
}
if( backColor.A == 0 )
{
// The back color is still transparent, so max the alpha component
backColor = Color.FromArgb( 255, backColor.R, backColor.G, backColor.B );
}
pevent.Graphics.Clear( backColor );
// Calculate the size of the box by using a large default character (since there could be no text
// in the control) and neither that ClientRectangle or pevent.ClipRectangle might have a valid value
var height = TextRenderer.MeasureText( pevent.Graphics, "X", Font ).Height;
var boxSide = (int)( height * factor );
var boxFrame = new Rectangle( 0, ( height - boxSide ) / 2, boxSide, boxSide );
var textFrame = new Rectangle( boxFrame.Right + 2, 0, ClientRectangle.Width - boxFrame.Right - 2, height );
if( Text.Length == 0 )
{
// The box cannot be aligned with non-existing text
boxFrame.Y = 0;
}
// Draw box
pevent.Graphics.SmoothingMode = SmoothingMode.HighQuality;
using var backBrush = new SolidBrush( BackColor );
using var foreBrush = new SolidBrush( ForeColor );
using var forePen = new Pen( ForeColor );
if( Enabled == false )
{
forePen.Color = foreBrush.Color = DisableFore;
}
pevent.Graphics.FillEllipse( backBrush, boxFrame );
pevent.Graphics.DrawEllipse( forePen, boxFrame );
if( Checked == true )
{
boxFrame.Offset( check, check );
boxFrame.Width -= check * 2;
boxFrame.Height -= check * 2;
pevent.Graphics.FillEllipse( foreBrush, boxFrame );
}
// Draw text
if( Text.Length != 0 )
{
using var sf = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center
};
TextRenderer.DrawText( pevent.Graphics,
Text,
Font,
textFrame,
foreBrush.Color,
TextFormatFlags.Top | TextFormatFlags.Left );
pevent.Graphics.SmoothingMode = SmoothingMode.None;
if( Focused == true )
{
textFrame.Inflate( -1, 0 );
ControlPaint.DrawFocusRectangle( pevent.Graphics, textFrame, foreBrush.Color, backBrush.Color );
}
}
pevent.Dispose();
}
}
view raw a.cs hosted with ❤ by GitHub

Comments