Custom Control Attributes

(March 2009 Issue)

Some hardcore developers prefer to write their code with Notepad rather than deal with the sometimes sluggish performance ofVisual Studio . However, the majority of .NET developers clearly appreciate the powerful interactive design-time development experience that Visual Studio can provide. To parlay this rich experience into the realm of custom Web controls, control developers should familiarize themselves with the key attributes that allow Visual Studio to do such impressive work.

 

Control Attributes

The code listed in Figure 1A should look rather ordinary to developers who have created custom Web controls. The first line of code specifies a design-time attribute that will be applied to the control definition that follows. As you can see, attributes are delimited by angle brackets in VB.NET. In C#, attributes are instead delimited by square brackets, as is shown in Figure 1B.

")> _

Public Class AttCtrl

   Inherits WebControl

       'TODO: implement control internals...

End Class

Figure 1A: Here, the ToolboxData attribute decorates this control s class definition.

[ToolboxData("<{0}:AttCtrl runat=server>")]

public class AttCtrl : WebControl

{

 //TODO: implement control internals...

}

Figure 1B: In contrast to VB.NET, attributes written in C# are delimited by square brackets and do not require line-continuation characters.

To provide a consistent development experience, the ToolboxData attribute must be attached to every custom Web control s class definition. The attribute s only parameter specifies the string that gets rendered to a Web form s ASPX definition each time application developers drag the control onto a page from the Visual Studio toolbox.

Additionally, there are a few other common control attributes often found decorating control definitions. For example, each control may optionally define one default event and property. Visual Studio keys off these attributes to provide an optimized development experience for the developers consuming the control. These attributes are summarized in Figure 2, along with the ToolboxBitmap attribute, which can be used to customize the control s Visual Studio toolbox icon.

Attribute Name

Description

DefaultEvent

Defines the control s most commonly consumed programmatic event (if any).

DefaultProperty

Defines the control s most commonly used design-time property (if any).

ToolboxData

Specifies the HTML tag that is generated when a new instance of the control is dragged from the toolbox onto a Web form.

ToolboxBitmap

Provided by the System.Drawing namespace as a way to specify the icon that should represent the control within the Visual Studio toolbox.

Figure 2: Common control attributes.

Niceties like this aren t strictly required in order to create a functional, reusable control that other developers find valuable. However, professional touches like these are what separates amateurs from professionals.

 

Property Attributes

Design-time attributes aren t limited to top-level control definitions. Each property exposed from within the control definition may also benefit from specially tuned design-time attributes. Figure 3 summarizes the design-time attributes most commonly applied to control properties.

Attribute Name

Description

Description

Used for describing the associated property.

Category

Specifies the category under which the associated property should appear in the Properties window.

Bindable

Enables/disables data-binding capabilities for the specified property.

Localizable

Enables/disables localization for the associated property.

DefaultValue

Specifies the property value that should be displayed when a custom value has not yet been set.

ReadOnly

Toggles the editability of the related property.

Browsable

When set to False, the associated property will not be displayed in the Properties window.

Obsolete

Provides a non-disruptive notification to consuming application developers that use of this property has been deprecated.

RefreshProperties

Commands the Properties window to fully refresh itself whenever the associated property value has changed.

MergableProperty

Allows the property to be changed in bulk when multiple instances of the control are selected.

UrlProperty

Enables invocation of the standard address chooser popup dialog box, which provides a more user friendly Web address selection experience.

NotifyParentProperty

Child properties can be configured to notify parent properties of any value modifications.

TypeConverter

Allows run-time conversion of value types.

DesignerSerializationVisibility

Configures the type of persistence that is applied upon serialization to the ASPX source document.

Figure 3: Common control property attributes.

The Description attribute is used to provide additional details about the intended use of the related property. The syntax is demonstrated in Figure 4.

 _

Property Text() As String

   Get

       'TODO...

   End Get

   Set(ByVal Value As String)

       'TODO...

   End Set

End Property

Figure 4: The Description attribute accepts a single string parameter.

When the property is selected in Visual Studio s Properties window, the specified description is displayed at the bottom of that window. Figure 5 illustrates this concept.


Figure 5: The value supplied by the Description attribute gets displayed at the bottom of the Properties window.

The Category attribute can be used to group related properties together in the Properties window. Commonly used property categories include Appearance, Behavior, and Data. For example, properties relating to colors, styles, and fonts are typically grouped together under the Appearance category. Because developers now habitually look for properties underneath these common categories, it is wise to place your custom properties in these categories, as well. However, there is nothing preventing the use of custom categories when the need arises. For example, the UserName property listed in Figure 6 will be grouped under a new custom category named User.

 _

Property UserName() As String

   Get

       'TODO...

   End Get

   Set(ByVal Value As String)

       'TODO...

   End Set

End Property

Figure 6: Multiple attributes may be applied to a single property, as long as they are separated by commas.

Figure 6 also demonstrates how multiple attributes may be applied to a single property, as long as all attributes are delineated by commas. It also should be noted that the entire attribute definition list (along with the first line of the property definition) must be written as a single logical line of code. This detail is inconsequential to C# developers, but to VB.NET developers it means making copious use of the line continuation character ( _ ) to keep code readable.

The Bindable attribute is used to specify that the associated property is a suitable target for data binding. If this attribute is excluded (or it is explicitly set to False), then the property will not be able to be programmatically manipulated via standard data binding techniques.

Localizable is another Boolean attribute that may be applied to control properties. If a property is decorated with this attribute (and the value of True is supplied as its sole parameter), then that property will participate normally in all localization-related activities. Otherwise, the property will not be localized. For example, a property such as JobTitle would likely make a good localization target, but attempts to localize a CustomerID property value would typically be an unnecessary waste of resources.

The DefaultValue attribute can be used to inform the Properties window what to display when the underlying property value has not yet been set. The attribute s heavily overloaded parameter list provides great flexibility.

Some properties are meant to be referenced, but not altered. Such properties can be decorated with the ReadOnly attribute, which expects a single Boolean parameter. Setting the ReadOnly design-time attribute to True will prevent the associated property from being modifiable in the Properties window at design time. However, it should be noted that this attribute has no effect on the run-time behavior of the property.

Some properties are relevant only under specific circumstances. For example, a HitCounter property may provide valuable metric data at run time, but at design time its value is moot. Therefore, it may be best to hide the property at design time by setting its Browsable attribute to False. This will prevent the property from appearing in the Properties window at design time, even though the property will be in place and fully functional at run time.

 

Soothe Growing Pains

As applications evolve, business rules change and underlying data structures often must be transformed to accommodate current needs. This may necessitate that preexisting control properties be added, deleted, or changed. Deleting an obsolete control property might sound harmless at first, but consider what happens when the new version of the control is deployed. Potentially, countless unsuspecting application developers may no longer be able to compile because their existing code references a property that no longer exists. Suddenly, a simple upgrade turns into a show-stopping integration incident that distracts everyone from their usual duties.

A better approach is to leave the old field as-is for a while, and provide a new property to support the new requirements. The old property then can be decorated with the Obsolete attribute, and upgrade instructions can be supplied as its parameter (see Figure 7). This will allow everybody s applications to continue compiling and running normally, even though they ll get nagging compile-time warnings as long as their code continues to reference the deprecated property. As time permits, each application developer will eventually modify their code to comply with the new standard, and the obsolete warnings will go away.

 _

Property UserName() As String

   Get

       Return _userName

   End Get

   Set(ByVal Value As String)

       _userName = Value

   End Set

End Property

Figure 7: Using the Obsolete attribute, deprecated properties can be eased out of production gradually to avoid jarring disruptions.

 

Advanced Tips & Tricks

Sometimes a control s properties are related to each other in deep and complex ways. Changing just a single property can trigger a cascade of events that may affect the values of many other properties. After a potentially complicated sequence of events like this it can be difficult to determine if the Properties window accurately reflects the current internal state of the control. In such circumstances, the RefreshProperties attribute can come in handy (see Figure 8). Any time the value changes of a property that s been decorated with this attribute, the Properties window will refresh. Therefore, decorating each relevant property with this attribute should eliminate any concerns about the Properties window getting out of sync.

 _

Property MyCustomProperty() As String

.

.

.

End Property

Figure 8: Example use of the RefreshProperties and MergableProperty attributes.

By default, if an application developer selects three instances of your custom control and tries to set a property value for all of them at once, the attempt will fail. For complex scenarios where control instances may share interdependent state, this is a reasonable default. However, for simple scenarios (like setting a common background color for all selected controls) it is good practice to allow the control consumer to edit as freely as they desire. The solution is to decorate the custom property with the MergableProperty attribute (again, see Figure 8). This will successfully allow changes to this property s value to propagate across each selected instance of the control.

Most property values can be displayed and manipulated directly in the Properties window. However, some kinds of data (like a series of charting data points) require more complex interactions than the simple property grid can support. Even relatively simple data entry chores can sometimes benefit from a well designed popup dialog box to help speed things along. For example, properties intended to hold URLs can benefit from the UrlProperty attribute. When a property decorated with this attribute is selected in the Properties window at design time, an ellipsis (...) button will appear. When clicked, the familiar dialog box shown in Figure 9 appears.


Figure 9: A single line of code is all it takes to invoke this common dialog box.

 

Conclusion

When it comes to creating and distributing custom Web controls, a little polish can go a long way. By decorating a control s classes and properties with design-time attributes, usability can be improved by leaps and bounds. Professional touches like these will make your control creations easier to use, resulting in saved time and money for everyone involved. It just makes good business sense.

Steve C. Orr is an ASPInsider, MCSD, Certified ScrumMaster, Microsoft MVP in ASP.NET, and author of Beginning ASP.NET 2.0 AJAX by Wrox. He s been developing software solutions for leading companies in the Seattle area for more than a decade. When he s not busy designing software systems or writing about them, he often can be found loitering at local user groups and habitually lurking in the ASP.NET newsgroup. Find out more about him at http://SteveOrr.net or e-mail him at mailto:[email protected].

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish