Create custom UI appearance for WinForms controls - part 1
By ganton ~ July 7th, 2008. Filed under: WinForms.
(All posts in this series: Post 1, Post 2, Post 3, Post 4, Post 5, Post 6, Source Code)
Today, I decided to write several posts about custom WinForms controls. In this first post I’ll explain what we will need to have a control which will be a fully customizable through Visual Studio (VS) designer.
Firstly, we will create a class which will hold our UI appearance settings. This class will be the base class for other classes which will contain much more information specific for some controls like CheckBox, RadioButton etc. For our base custom UI appearance we will support border Color, background Color, border thickness, enable/disable AntiAlias. This class is named CustomBaseUIAppearance and it is listed below. There are few things which should be noted. You can see that we have a variable “_owner” which is of type Control (line 5). We will use it when a property value is changed to inform parent control for the change (line 27 - 31). Now let’s go through attributes which decorate class’ properies (line 17). DescriptionAttribute is used to tell VS’ designer what to display in description area of property grid. CategoryAttribute tells VS’ designer that this property should be placed under specified category in property grid. DefaultValueAttribute informs the designer which is the default value of current property.
<br>
[TypeConverter(typeof(CustomBaseUIAppearanceTypeConverter))]<br>
public class CustomBaseUIAppearance<br>
{<br>
private Control _owner;<br>
private Color _borderColor;<br>
private Color _backColor;<br>
private int _borderThicness;<br>
private bool _enableAntiAlias;<br>
protected Control Owner<br>
{<br>
get<br>
{<br>
return _owner;<br>
}<br>
}<br>
[Description("Gets or Sets control's border color. It can not be set to Color.Transparent."), Category("Custom Appearance"), DefaultValue(typeof(Color), "Black")]<br>
public Color BorderColor<br>
{<br>
get { return _borderColor; }<br>
set<br>
{<br>
if (value.Equals(Color.Transparent))<br>
{<br>
throw new ArgumentOutOfRangeException("BorderColor", "Parameter cannot be set to Color.Transparent");<br>
}<br>
if (!_borderColor.Equals(value))<br>
{<br>
_borderColor = value;<br>
_owner.Invalidate();<br>
}<br>
}<br>
}<br>
[Description("Gets or Sets control's background color. It can not be set to Color.Transparent."), Category("Custom Appearance"), DefaultValue(typeof(Color), "White")]<br>
public Color BackColor<br>
{<br>
get { return _backColor; }<br>
set<br>
{<br>
if (value.Equals(Color.Transparent))<br>
{<br>
throw new ArgumentOutOfRangeException("BackColor", "Parameter cannot be set to Color.Transparent");<br>
}<br>
if (!_backColor.Equals(value))<br>
{<br>
_backColor = value;<br>
_owner.Invalidate();<br>
}<br>
}<br>
}<br>
[Description("Gets or Sets control's border thickness."), Category("Custom Appearance"), DefaultValue(1)]<br>
public int BorderThicness<br>
{<br>
get { return _borderThicness; }<br>
set<br>
{<br>
if (value < 1)<br>
{<br>
throw new ArgumentOutOfRangeException("BorderThicness", "Parameter must be set to a valid integer value hihger than 0.");<br>
}<br>
if (_borderThicness != value)<br>
{<br>
_borderThicness = value;<br>
_owner.Invalidate();<br>
}<br>
}<br>
}<br>
[Description("If true control's is drawn using antialiasing, otherwise antialiasing is not enabled."), Category("Custom Appearance"), DefaultValue(true)]<br>
public bool EnableAntiAlias<br>
{<br>
get { return _enableAntiAlias; }<br>
set<br>
{<br>
if (_enableAntiAlias != value)<br>
{<br>
_enableAntiAlias = value;<br>
_owner.Invalidate();<br>
}<br>
}<br>
}<br>
public CustomBaseUIAppearance(Control owner)<br>
{<br>
if (owner == null)<br>
{<br>
throw new ArgumentNullException("owner");<br>
}<br>
_owner = owner;<br>
_borderColor = Color.Black;<br>
_backColor = Color.White;<br>
_borderThicness = 1;<br>
_enableAntiAlias = true;<br>
}<br>
}<br>
May be the most interesting attribute is TypeConverterAttribute used to decorate our class.
Specifies what type to use as a converter for the object this attribute is bound to.
We provide it with our custom type converter class presented in next snippet.
<br>
public class CustomBaseUIAppearanceTypeConverter : ExpandableObjectConverter<br>
{<br>
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)<br>
{<br>
// if destination type is the type we expected try to convert<br>
if (destinationType == typeof(string))<br>
{<br>
CustomBaseUIAppearance appearance = value as CustomBaseUIAppearance;<br>
// if the value is of type appearance as we need it<br>
if (appearance != null)<br>
{<br>
return string.Format("Back {0}; Border {1}; Thickness [{2}]", appearance.BackColor.ToString(),<br>
appearance.BorderColor.ToString(), appearance.BorderThicness.ToString());<br>
}<br>
}<br>
return base.ConvertTo(context, culture, value, destinationType);<br>
}<br>
}<br>
The only overriden method is ConvertTo(). It will return a string presentation of part of the UI appearance information this class contains.
This information will be used by the VS’ designer to show it under property grid as in the picture belsow.

This picture shows how looks our appearance class. Actually it is a view of a CustomCheckBoxUIAppearance class derived from CustomBaseUIAppearance class.
We will talk about it in my next post where I’ll wrote how to create a check box with custom look.
July 10th, 2008 at 5:40 pm
[...] Create custom UI appearance for WinForms controls - part 1 [...]
July 11th, 2008 at 4:45 pm
[...] Create custom UI appearance for WinForms controls - part 1 [...]
July 11th, 2008 at 4:45 pm
[...] Create custom UI appearance for WinForms controls - part 1 [...]
July 30th, 2008 at 5:46 pm
[...] posts in this series: Post 1, Post 2, Post 3, Post 4, Source [...]
July 31st, 2008 at 3:06 pm
[...] posts in this series: Post 1, Post 2, Post 3, Post 4, Post 5, Post 6, Source [...]