
Introduction
When a customer looks at an application for the first time, they make a lot of decisions based upon the overall appearance of the application. To a developer this can be very frustrating. Many people don’t care if the form that is launched by a button click took ten hours to code. They only care about the appearance. Recently I was getting ready for a software rollout. During my testing and QA I noticed that the plain old Label
component bundled in the .NET framework just was too plain to use a banner across the top of my forms. Therefore in order to help the appearance of my form I created a label that can draw a gradient background and some 3D text.
Using the code
I started by creating a new component that derives from the standard System.Windows.Forms.Label
control. Then I overrode the Paint
method. Originally I hard coded the colors used in the gradient, drop shadow color etc.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint (e);
e.Graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
LinearGradientBrush brush = new LinearGradientBrush (new
Rectangle( 0,0, this.Width, this.Height),
Color.White, Color.LightSkyBlue, 0, true );
e.Graphics.FillRectangle( brush, 0, 0, this.Width, this.Height );
e.Graphics.DrawString( this.Text, this.Font,
new SolidBrush( Color.Black ), 1, 1,
StringFormat.GenericDefault );
e.Graphics.DrawString( this.Text, this.Font,
new SolidBrush( this.ForeColor ), 0, 0,
StringFormat.GenericDefault );
}
After using my control for a while, I decided that I needed to be able to change several properties of my control using the property editor. So I added some private variables and made public properties for them in order for them to appear in the property grid.
private bool _drawGradient = true;
private Color _startColor = Color.White;
private Color _endColor = Color.LightSkyBlue;
private float _angle = 0;
private bool _drawShadow = true;
private float _yOffset = 1;
private float _xOffset = 1;
private Color _shadowColor = Color.Black;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint (e);
e.Graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if( _drawGradient == true )
{
LinearGradientBrush brush = new LinearGradientBrush (new
Rectangle( 0,0, this.Width, this.Height),
_startColor, _endColor, _angle, true );
e.Graphics.FillRectangle( brush, 0, 0,
this.Width, this.Height );
}
if( _drawShadow == true )
e.Graphics.DrawString( this.Text, this.Font,
new SolidBrush( _shadowColor ), _xOffset,
_yOffset, StringFormat.GenericDefault );
e.Graphics.DrawString( this.Text, this.Font,
new SolidBrush( this.ForeColor ), 0, 0,
StringFormat.GenericDefault );
}
If you look at the set
methods of the variables used by the Paint
method you will notice that whenever I update a value I immediately call the Invalidate()
method. If you don’t call this method you will not see your changes in design mode as you modify properties.
public bool DrawGradient
{
get{ return this._drawGradient; }
set{ this._drawGradient = value; this.Invalidate(); }
}
In addition to adding design time support by exposing the properties of the component, I also added attributes to the properties. The use of attributes allowed me to specify which category, description and default values (used when the user right clicks in the property grid to reset a value. Modified properties are shown in bold.) would be used when viewing a property in the property grid.
[ Category("Gradient"),
Description("Set to true to draw the gradient background"),
DefaultValue(true)]
public bool DrawGradient
{
get{ return this._drawGradient; }
set{ this._drawGradient = value; this.Invalidate(); }
}
I hope someone else has a use for this control. Feel free to add comments and suggestions. The feedback is always welcome.