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(); | |
} | |
} |