Sunday, December 20, 2009

DefaultValue for Properties

When in the Designer for a Form, everyone knows that you can go to the Properties window and change a property on the form or a control. You most likely also know that you can then right-click on any changed property, and choose "Reset" to set it back to its Default Value.

What code do you need in your own controls to get that to work? You need code in two places: in the Constructor to set the value to begin with, and a [DefaultValue] attribute for the Property itself:

public class MyTextBox : TextBox
{
private string m_MyProperty;

public MyTextBox
{
this.m_MyProperty = "";

// As an additional bonus, I'm showing you two ways to do color
this.BackColor = System.Drawing.Color.FromArgb(90, 100, 240);
this.ForeColor = System.Drawing.Color.Firebrick; ;
}

[DefaultValue("")]
public string MyProperty
{
get {return this.m_MyProperty;}
set {this.m_MyProperty = value;}
}
[DefaultValue(typeof(System.Drawing.Color), "90,100,240")]
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
base.BackColor = value;
}
}
[DefaultValue(typeof(System.Drawing.Color), "Firebrick")]
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
base.ForeColor = value;
}
}
}

Some properties aren't virtual and can't be overridden. For those properties, you can use "new" instead of "override".

The [DefaultValue] attribute serves two purposes:

  1. It allows the property to be easily Reset to it's Default Value from the Property Sheet.
  2. It prevents the code that sets the default value from actually showing up in the InitializeComponent() method.

That second point is pretty important. Let me illustrate why. Say that you've not used the [DefaultValue] attribute in your TextBox, but simply set the value of the BackColor and ForeColor properties in your MyTextBox constructor, like this:

public class MyTextBox : TextBox
{
public MyTextBox()
{
this.BackColor = Color.DarkGreen;
this.ForeColor = Color.Firebrick;
}
}

When you drop this TextBox onto a Form, the BackColor and ForeColor will be explicitly set in the code  generated in the  InitializeComponent() method of the Form (IOW, you’ll have the code to set BackColor and ForeColor for every TextBox you drop on your design surface). Consequently, if you later decide to change the color in your MyTextBox class, you have to revisit every single Form that you ever dropped a MyTextBox on, to change it to the new default value. Not good!

Conversely, let's look at the opposite scenario where you do include a property with a [DefaultValue] attribute, but you don't initialize that in the constructor. So, your class looks like this:

public class MyTextBox : TextBox
{
// bad code! Do not try this at home! ;0)
public MyTextBox()
{
}

[DefaultValue(typeof(System.Drawing.Color), "DarkGreen")]
public override System.Drawing.Color BackColor
{
get {return base.BackColor;}
set {base.BackColor = value;}
}
[DefaultValue(typeof(System.Drawing.Color), "Firebrick")]
public override System.Drawing.Color ForeColor
{
get {return base.ForeColor;}
set {base.ForeColor = value;}
}
}

When you drop MyTextBox on a Form now, it will default to a color of SystemColors.Control for the BackColor property and SystemColors.WindowText for the ForeColor property, and the IDE will generate the code in the InitializeComponent() method of the Form where you dropped MyTextBox onto, unless you go to the Property Sheet, right-click the BackColor and choose "Reset" (and likewise for ForeColor). Once you do this, the correct color appears for the TextBox in the Designer, the IDE removes the setting of the BackColor and ForeColor properties in the InitializeComponent() method and all looks fine ... until you decide to change the default to something else in your MyTextBox class ... since your TextBox doesn't initialize its BackColor and ForeColor properties in its constructor, the Form goes back to displaying SystemColors.Control and SystemColors.WindowText (even though it's not hard-coded in the InitializeComponent() method).  Again, this is not good!

Suffice it to say that you simply must do both -- initialize it in the constructor and specify the [DefaultValue] attribute. Just something that you're going to have to remember to do!!

2 comments:

  1. your blog seems really interesting. I gotta keep an eye on it. :) Thanks a lot.

    ReplyDelete
  2. Thanks for the kind words, Tanya. I hope you visit regularly (although my posts aren't as frequent as I'd like them to be).

    ReplyDelete