Design-time properties for custom controls (Windows Forms .NET)

This article teaches you about how properties are handled for controls in the Windows Forms Visual Designer in Visual Studio.

Every control inherits many properties from the base class System.Windows.Forms.Control, such as:

When creating a control, you can define new properties and control how they appear in the designer.

Define a property

Any public property with a get accessor defined by a control is automatically visible in the Visual Studio Properties window. If the property also defines a set accessor, the property can be changed in the Properties window. However, properties can be explicitly displayed or hidden from the Properties window by applying the BrowsableAttribute. This attribute takes a single boolean parameter to indicate whether or not it's displayed. For more information about attributes, see Attributes (C#) or Attributes overview (Visual Basic).

[Browsable(false)]
public bool IsSelected { get; set; }
<Browsable(False)>
Public Property IsSelected As Boolean

[NOTE] Complex properties that can't be implicitly converted to and from a string require a type converter.

Serialized properties

Properties set on a control are serialized into the designer's code-behind file. This happens when the value of a property is set to something other than its default value.

When the designer detects a change to a property, it evaluates all properties for the control and serializes any property whose value doesn't match the default value for the property. The value of a property is serialized into the designer's code-behid file. Default values help the designer to determine which property values should be serialized.

Default values

A property is considered to have a default value when it either applies the DefaultValueAttribute attribute, or the property's class contains property-specific Reset and ShouldSerialize methods. For more information about attributes, see Attributes (C#) or Attributes overview (Visual Basic).

By setting a default value, you enable the following:

  • The property provides visual indication in the Properties window if it has been modified from its default value.
  • The user can right-click on the property and choose Reset to restore the property to its default value.
  • The designer generates more efficient code.

If a property uses a simple type, such as a primitive type, the default value can be set by applying the DefaultValueAttribute to the property. However, properties with this attribute don't automatically start with that assigned value. You must set the property's backing field to the same default value. You can set the property on the declaration or in the class's constructor.

When a property is a complex type, or you want to control the designer's reset and serialization behavior, define the Reset<PropertyName> and ShouldSerialize<PropertyName> methods on the class. For example, if the control defines an Age property, the methods are named ResetAge and ShouldSerializeAge.

Important

Either apply the DefaultValueAttribute to the property, or provide both Reset<PropertyName> and ShouldSerialize<PropertyName> methods. Don't use both.

Properties can be "reset" to their default values through the Properties window by right-clicking on the property name and selecting Reset.

The Reset context menu item in the properties grid.

The availability of the Properties > Right-click > Reset context menu option is enabled when:

  • The property has the DefaultValueAttribute attribute applied, and the value of the property doesn't match the attribute's value.
  • The property's class defines a Reset<PropertyName> method without a ShouldSerialize<PropertyName>.
  • The property's class defines a Reset<PropertyName> method and the ShouldSerialize<PropertyName> returns true.

DefaultValueAttribute

If a property's value doesn't match the value provided by DefaultValueAttribute, the property is considered changed and can be reset through the Properties window.

Important

This attribute shouldn't be used on properties that have corresponding Reset<PropertyName> and ShouldSerialize<PropertyName> methods.

The following code declares two properties, an enumeration with a default value of North and an integer with a default value of 10.

[DefaultValue(typeof(Directions), "North")]
public Directions PointerDirection { get; set; } = Directions.North;

[DefaultValue(10)]
public int DistanceInFeet { get; set; } = 10;
<DefaultValue(GetType(Directions), "North")>
Public Property PointerDirection As Directions = Directions.North

<DefaultValue(10)>
Public Property DistanceInFeet As Integer = 10

Reset and ShouldSerialize

As previously mentioned, the Reset<PropertyName> and ShouldSerialize<PropertyName> methods provide the opportunity to guide not only the reset behavior of a property, but also in determining if a value is changed and should be serialized into the designer's code-behind file. Both methods work together and you shouldn't define one without the other.

Important

The Reset<PropertyName> and ShouldSerialize<PropertyName> methods shouldn't be created for a property that has a DefaultValueAttribute.

When Reset<PropertyName> is defined, the Properties window displays a Reset context menu option for that property. When Reset is selected, the Reset<PropertyName> method is invoked. The Reset context menu option is enabled or disabled by what is returned by the ShouldSerialize<PropertyName> method. When ShouldSerialize<PropertyName> returns true, it indicates that the property has changed from its default value and should be serialized into the code-behind file and enables the Reset context menu option. When false is returned, the Reset context menu option is disabled and the code-behind has the property-set code removed.

Tip

Both methods can and should be defined with private scope so that they don't make up the public API of the control.

The following code snippet declares a property named Direction. This property's designer behavior is controlled by the ResetDirection and ShouldSerializeDirection methods.

public Directions Direction { get; set; } = Directions.None;

private void ResetDirection() =>
    Direction = Directions.None;

private bool ShouldSerializeDirection() =>
    Direction != Directions.None;
Public Property Direction As Directions = Directions.None

Private Sub ResetDirection()
    Direction = Directions.None
End Sub

Private Function ShouldSerializeDirection() As Boolean
    Return Direction <> Directions.None
End Function

Type converters

While type converters typically convert one type to another, they also provide string-to-value conversion for the property grid and other design-time controls. String-to-value conversion allows complex properties to be represented in these design-time controls.

Most built-in data types (numbers, enumerations, and others) have default type converters that provide string-to-value conversions and perform validation checks. The default type converters are in the System.ComponentModel namespace and are named after the type being converted. The converter type names use the following format: {type name}Converter. For example, StringConverter, TimeSpanConverter, and Int32Converter.

Type converters are used extensively at design-time with the Properties window. A type converter can be applied to a property or a type, using the TypeConverterAttribute.

The Properties window uses converters to display the property as a string value when the TypeConverterAttribute is declared on the property. When the TypeConverterAttribute is declared on a type, the Properties window uses the converter on every property of that type. The type converter also helps with serializing the property value in the designer's code-behind file.

Type editors

The Properties window automatically uses a type editor for a property when the type of the property is a built-in or known type. For example, a boolean value is edited as a combo box with True and False values and the DateTime type uses a calendar dropdown.

Important

Custom type editors have changed since .NET Framework. For more information, see The designer changes since .NET Framework (Windows Forms .NET).