Version 2 is an improved version using a transparent panel.
Introduction
When applying a dark skin theme to my Windows Forms application which used a GroupBox
, everything seemed fine, see screendump below:
But as soon as I wanted to set the GroupBox.Enabled
property to false
, things did not look that good anymore.
As can be seen in the screendump, the standard GroupBox
on the right automatically chooses a black color for the disabled state:
Background
SkinSettings Class
The skin themes are in a separate class SkinSettings
, to keep the example small, it only has a few settings.
It also has a ColorDisabled()
method for calculating the custom disabled Color
.
using System;
using System.Drawing;
namespace CustomControls1
{
public static class SkinSettings
{
public static Color FormBackColor { get; set; }
public static Color FormForeColor { get; set; }
public static Color FontColor { get; set; }
static SkinSettings()
{
SetBlueTheme();
}
public static void SetBlueTheme()
{
FormBackColor = Color.FromArgb(50, 100, 150);
FormForeColor = Color.DarkBlue;
FontColor = Color.LightGray;
}
public static void SetGrayTheme()
{
FormBackColor = Color.FromArgb(200, 200, 200);
FormForeColor = Color.Black;
FontColor = Color.Black;
}
public static Color ColorDisabled(Color color)
{
int red = Math.Min(255, color.R + 40);
int green = Math.Min(255, color.G + 40);
int blue = Math.Min(255, color.B + 40);
return Color.FromArgb(red, green, blue);
}
}
}
CustomGroupBox Class
The CustomGroupBox
class is in fact an extended version of the standard GroupBox
control, note that you need to build the project first before it will appear in the Toolbox. It can be used just like a normal GroupBox
.
It handles RadioButtons
and CheckBoxes
events itself using the SetEventHandlers()
method which is called at runtime when the CustomGroupBox
is selected.
It also has its own Enabled
property which overrides the default Enabled
behaviour (using the new
keyword) which causes the ugly black colors.
namespace CustomControls1
{
using System;
using System.Drawing;
using System.Windows.Forms;
public class CustomGroupBox : GroupBox
{
private bool enabled = true;
private bool setEventHandlers;
public CustomGroupBox()
{
this.ForeColor = SkinSettings.FormForeColor;
}
public new bool Enabled
{
get
{
return this.enabled;
}
set
{
this.enabled = value;
this.UpdateControls(value);
}
}
private void UpdateControls(bool enabledLocal)
{
if (enabledLocal)
{
this.ForeColor = SkinSettings.FontColor;
}
else
{
this.ForeColor = SkinSettings.ColorDisabled(this.BackColor);
}
foreach (var control in this.Controls)
{
if (control is TextBox)
{
var txt = control as TextBox;
txt.Enabled = enabledLocal;
}
if (control is Button)
{
var btn = control as Button;
btn.Enabled = enabledLocal;
}
}
}
private void CheckBoxClick(object sender, EventArgs e)
{
var cb = sender as CheckBox;
if (this.enabled && cb != null)
{
cb.Checked = !cb.Checked;
}
}
private void RadioButtonClick(object sender, EventArgs e)
{
var rb = sender as RadioButton;
if (this.enabled && rb != null)
{
foreach (var control in rb.Parent.Controls)
{
if (control is RadioButton)
{
var rb2 = control as RadioButton;
rb2.Checked = false;
}
}
rb.Checked = true;
}
}
protected override void OnEnter(EventArgs e)
{
if (!setEventHandlers)
{
this.SetEventHandlers();
}
base.OnEnter(e);
}
private void SetEventHandlers()
{
this.setEventHandlers = true;
foreach (var control in this.Controls)
{
if (control is CheckBox)
{
var chk = control as CheckBox;
chk.AutoCheck = false;
chk.Click += new System.EventHandler(this.CheckBoxClick);
}
if (control is RadioButton)
{
var rb = control as RadioButton;
rb.AutoCheck = false;
rb.Click += new System.EventHandler(this.RadioButtonClick);
}
}
}
}
}
The Form
The main form has a CustomGroupBox
and a standard GroupBox
. Nothing shocking here as most things are handled by the other classes:
namespace CustomControls1
{
using System;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
this.InitializeComponent();
this.SetColors();
}
private void button1_Click(object sender, EventArgs e)
{
this.groupBox2.Enabled = !this.groupBox2.Enabled;
this.customGroupBox1.Enabled = !this.customGroupBox1.Enabled;
if (this.groupBox2.Enabled)
{
this.Text = "Custom groupbox enabled";
}
else
{
this.Text = "Custom groupbox disabled";
}
}
private void button2_Click(object sender, EventArgs e)
{
SkinSettings.SetBlueTheme();
this.SetColors();
}
private void button3_Click(object sender, EventArgs e)
{
SkinSettings.SetGrayTheme();
this.SetColors();
}
private void SetColors()
{
this.BackColor = SkinSettings.FormBackColor;
this.ForeColor = SkinSettings.FormForeColor; this.groupBox2.ForeColor = SkinSettings.FontColor;
this.customGroupBox1.ForeColor = SkinSettings.FontColor;
}
}
}
Using the Code
I tested the application with VS2013, .NET 4.5 and Windows 7, but I'm pretty sure it will work with older versions too.
Points of Interest
For a larger scale application, the SkinSettings
class needs to be extended to support more Control types like Panel
, TextBox
, ToolStrip
, etc.
I also implemented a method that can apply the SkinSettings
to a whole Form by setting the colors in a loop that goes through all Controls in the Forms Control collection. To keep the example readable, this method was omitted.
History
- 13 June 2015: First version published
- 14 June 2015: Improved version using transparent panel