Creating Custom Controls in Winforms - Part 4 - Adding a property: DefaultValue attribute explained

Debunking a common misconception

There is a lot of confusion around the DefaultValue attribute - it feels like it really should setup the initial value of the property. Unfortunately, this is not the case. It’s used to determine whether the property should be written out to InitializeComponent, and what to reset the property to when someone right clicks in the property grid and selects “Reset”.

Continuing on from last time with the square control, adding a new property


Perhaps we don’t *always* want the hover rect to paint on mouse over. We can give people the option of having that on by adding another property called “Popup”.

private bool popup = false;

public bool Popup {

get {

return popup;

}

set {

popup = value;

}

}

In OnPaint we’ll check this property to see whether or not we should actually paint the rectangle.

When the square control is dropped onto the design surface, the following lines of code are generated:

this.square1.Location = new System.Drawing.Point(96, 80);

this.square1.Name = "square1";

this.square1.Popup = false;

this.square1.TabIndex = 2;

this.square1.Text = "square1";

While this is correct, we don’t actually want initialize component to call the Popup method, because it’s not going to change anything… one could imagine perf problems if setting this back in was a slow operation.

Common Misconception:

Specifying the DefaultValue does NOT set the value of your property – it’s used to determine whether the property should be written out to InitializeComponent, and what to reset the property to when someone right clicks in the property grid and selects “Reset”.

If we add the DefaultValueAttribute to the top of the property declaration, we can essentially inform the serializer what the untouched value of the property would be (if no one ever played with it). The serializer will then skip over properties that haven’t changed.

[DefaultValue(false)]

public bool Popup {

get {

return popup;

}

set {

popup = value;

}

}

This is an alternative to using the ShouldSerialize and Reset method pattern. If you wanted to do this using ShouldSerialize and Reset you would add these two functions to the square class:

private bool ShouldSerializePopup() {

return Popup != false;

}

private void ResetPopup() {

Popup = false;

}

Note these methods do not need to be public, as the serializer finds these methods via reflection. The Reset method we have not talked about yet – this is what gets called when someone right clicks on the property in the PropertyGrid and chooses the “Reset” context menu item. In general prefer DefaultValue to the ShouldSerialize method.

Ragha posted an article about how the CodeDomSerializer figures all this out a while back.