Introduction
Some Windows Forms controls in .NET don't allow us to change the BackColor
. This is true in the case of DateTimePicker
. Another known problem is the poor readability of the text when the controls are disabled. In this example, I have derived such a class introducing the ability to set BackColor
, and I've added a new property BackDisabledColor
.
Background
The BackColor
property is defined in the base class Control
, and therefore is available in the derived classes. Also, such a property is usually available in the Properties window at design-time. However, the standard DateTimePicker
is not able to draw a user chosen background color. If we set it, nothing happens. So, in the DateTimePicker
class, this property is overridden with the attribute [Browsable(false)]
. In this way, we don't have to see a useless property.
The DateTimePicker
control uses some Windows features to draw the control. So, the WM_PAINT
message is not managed by OnPaint
method of the class itself. The OnPaint
method is never called. To force calling that method in our derived class, we have to set the control style to UserPaint
in the constructor:
this.SetStyle(ControlStyles.UserPaint, true);
Now, we can use our OnPaint
, but we cannot take advantage of the base OnPaint
because it does nothing. To check this, try the following code:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
base.OnPaint(e);
}
You will see the control showing nothing. So now, we have the entire responsibility of drawing the control. In order to keep a useful and clear code, we abandon two original features otherwise requiring a lot of coding. The first one is the optional checkbox used to allow end-users to skip the displayed date (the ShowCheckBox
property). The second is the ability to enter date parts directly using keyboard and not through a calendar.
Using the Code
To use the code, simply add the DateTimePicker.cs file to your project and use the BCDateTimePicker
just like its base class.
The main work is done by an override of the OnPaint
method. Here, we put the code to draw the background rectangle, filled with the color we want, and then draw the drop down button and the text representing the date. The work of drawing the drop down button is done by ComboBoxRenderer.DrawDropDownButton
.
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics g = this.CreateGraphics();
Rectangle dropDownRectangle =
new Rectangle(ClientRectangle.Width - 17, 0, 17, 16);
Brush bkgBrush;
ComboBoxState visualState;
if (this.Enabled) {
bkgBrush = new SolidBrush(this.BackColor);
visualState = ComboBoxState.Normal;
}
else {
bkgBrush = new SolidBrush(this._backDisabledColor);
visualState = ComboBoxState.Disabled;
}
g.FillRectangle(bkgBrush, 0, 0, ClientRectangle.Width, ClientRectangle.Height);
g.DrawString(this.Text, this.Font, Brushes.Black, 0, 2);
ComboBoxRenderer.DrawDropDownButton(g, dropDownRectangle, visualState);
g.Dispose();
bkgBrush.Dispose();
}
In order to show the BackColor
property in the Properties window again, we override it, setting the Browsable
attribute to true
.
[Browsable(true)]
public override Color BackColor
{
get { return base.BackColor; }
set { base.BackColor = value; }
}
Also, we create a BackDisabledColor
property allowing the developer to choose the color at design-time.
[Category("Appearance"),
Description("The background color of the component when disabled")]
[Browsable(true)]
public Color BackDisabledColor
{
get { return _backDisabledColor; }
set { _backDisabledColor = value; }
}
Points of Interest
In the overridden OnPaint
method, I've created a new Graphics
object from the control rather than use the one returned by PaintEventArgs
. In fact, when using the ShowUpDown
mode, the base class invalidates only the date part region involved by user interaction. When using the long format, this produces an incorrect text display. Since I've found no other way to extend the clipping region of the Graphics
object passed by the event argument, the solution adopted is to create a new Graphics
, allowing to draw in the whole extent of the control.