Decorate Your Code with Astonishing Attributes

hen I first saw attributes in Visual Studio .NET, I thought they were some sort of oddity Microsoft had dragged in from Java, C++, or perhaps the Delta Quadrant. They clearly aren’t Visual Basic code. To someone who uses angle brackets only for comparing two numbers, this Property statement, Public Property Salary() As Decimal, looks like it’s been infected by the Borg!

In fact, attributes don’t look like code because they aren’t code. They’re really code “decorations” that supply extra information related to the code to anyone or any process that cares. Generally, these attributes are useful to entities that manipulate code and objects in interesting ways?such as Visual Studio’s Properties window, the debugger, and XML serializers.

After I got over the shock of seeing the suspicious brackets (or “wakkas” to give them their cool and edgy name) scattered throughout my code, I discovered that they can make using and understanding code a lot easier.

Before I start talking about specific attributes, you should learn a few facts about attributes in general. An attribute is an object. It is an instance of a class with a name that ends in “Attribute.” In Visual Basic, you can omit the “Attribute” part when you use an attribute statement. For example, instead of using “DescriptionAttribute” inside the wakkas you can use “Description.” You still need to know that the word “Attribute” belongs at the end of the class name, however, so you can look them up in the online help.

Because attributes are not really part of the language, and because they can have effects at design time when your code isn’t running, you don’t create them the same way you would create normal objects. Instead you include a call to the attribute class’s constructor in angle brackets preceding the code item the attribute decorates.

For example, the following code shows a DescriptionAttribute object applied to a property declaration. Notice that the attribute statement’s line ends with a line continuation character; in other words, syntactically it’s part of the same line as the Property statement.

    _   Public Property LastName() As String       ...   End Property

The preceding code uses a Description constructor that takes a single parameter: a string giving the property’s description. If you select an object in the form designer that has this property, the text “The Employee’s last or family name.” appears in the description area at the bottom of the Properties window.

Author’s Note: C# uses square brackets rather than angle brackets and doesn’t require a line continuation character.

Attributes can be applied only to code item types for which they were intended. For example, the ToolboxBitmap attribute described later in this article tells Visual Studio which bitmap to use to represent a component or control in the Toolbox and Component Tray. Because the attribute applies only to classes, you cannot apply it to a property, method, or event?only classes. If you try to add a ToolboxBitmap to a function, Visual Studio flags it as an error.

You can separate multiple attribute constructors by commas within a single set of angle brackets like this.

    _   Public Property LastName() As String       ...   End Property

Alternatively, you can put multiple attributes inside their own brackets:

    _    _   Public Property LastName() As String       ...   End Property

I prefer the second style because it makes adding and removing attributes easier. You can simply paste one in or cut one out of any position in the list without messing up the brackets around the others.

Now that you know the basics, the rest of this article discusses some property-related and XML serialization attributes that you may find useful.

Property Window Attributes
Microsoft has defined a fairly large assortment of attributes to give extra information to the Properties window. They give hints about how the window should display information about properties and how to interact with pieces of your code. These attributes are defined in the System.ComponentModel namespace so you can make your life a little easier if you Import that namespace before you get started.

The PropertyGrid control is basically its own little Properties window so it also honors these attributes (mostly). That means, for example, that if you create a Customer class and let the user modify Customer objects at run time in a PropertyGrid, you can use these attributes to influence how the control behaves.

The following list describes some of the most useful of the property-oriented attributes.

Browsable?This tells the Properties window whether to display a property. If you set this to False, code can get and set the property at run time but developers cannot see or change the value in the Properties window. For example, the following code makes the Price property non-browsable.

    _   Public Property Price() As Decimal       ...   End Property

Category?This sets the category in which the Properties window will group a property or event when displaying items grouped by category rather than sorted alphabetically. You can put any string in the constructor; the Properties window will create a new category if one with that name doesn’t already exist.

DefaultEvent?This tells designers which of this class’s events is most commonly used. If you double-click on an object in the form designer, for example, the code editor opens to this event. If you click the Properties window’s Events button and then select an object from this class, it’s also the event that the window initially selects.

DefaultValue?This sets a default value for a property. If you right-click the property in the Properties window and select Reset, the property gets this value. This is particularly useful for properties that take objects as values because you can’t just type a new value into the property. Normally you set an object’s default value to Nothing as in the following code.

    _   Public Property Picture() As Image       ...   End Property

Description?This gives the description that the Properties window displays for a property.

Localizable?This indicates whether the property should be localizable. If you set this to True, then Visual Studio automatically saves the property’s value in localized resource files rather than in the form’s shared resource file.

ParenthesizePropertyName?This tells the Properties window whether to surround the property’s name with parentheses. Parenthesized properties are shown at the top of the window?or at the top of their category if the list is grouped by category.

[ReadOnly]?This tells the Properties window that a property is read-only. The window displays the property grayed out and doesn’t let users change its value. Because ReadOnly is also a Visual Basic keyword, you must enclose this attribute’s name in square brackets so Visual Basic doesn’t get confused.

Note that you can achieve a similar effect by creating a read-only property as in the following code:

   Public ReadOnly Property FirstNameLast() As String       ...   End Property

The difference is that when you use the [ReadOnly] attribute, you can define both property get and set routines in the code, meaning that code can set and retrieve the property at run time but the Properties window won’t let developers set it at design time. In contrast, when you create a read-only property, you can’t set the value from code either.

RefreshPropertiesAttribute?This tells the Properties window whether it should refresh the object’s other properties whenever this property’s value changes. For example, suppose an Employee class has FirstName and LastName properties. Suppose it also provides a FullName property that gets and sets both of those properties in the format “FirstName LastName.” If a developer changes either the FirstName or LastName value, then the Properties window must update its display of the FullName property. Similarly if the developer changes the FullName value, the window must update its FirstName and LastName values. To force these updates to occur, you can set the RefreshPropertiesAttribute to True for all three properties.

?
Figure 1. Employee Object Properties: The figure shows the property-related attributes in action in the Properties pane in Visual Studio.

ToolboxBitmap?This sets which bitmap the IDE will use to display an object of this class in the Toolbox or in a form’s component tray. (Okay, this isn’t exactly a property attribute but it’s close.) Unfortunately this attribute is a bit tricky to use. The most straightforward of its overloaded constructors takes a parameter giving the full path to the bitmap file to use. However, because you can’t always count on an external bitmap file being present, a more flexible (but more confusing) version takes the type of a class in the assembly containing a bitmap resource plus the name of the resource. To use this version, you must also set the resource’s Build Action property to Embedded Resource.

ToolboxItem?This tells the IDE whether to display an icon for a control or component in the Toolbox (another non-property attribute). This attribute is useful if you want to create a control for your own use but don’t want to make it easily available to developers using your library. If you set this attribute to False, your code can use it but it won’t appear in the Toolbox.

Listing 1 shows an Employee class that demonstrates most of these properties. You’ll find the class in the downloadable sample code for this article that you can experiment with to see how the attributes affect the Properties window at design time and the program’s PropertyGrid control at run time.

Figure 1 shows a program with a PropertyGrid control displaying the properties for an Employee object. At the bottom you can see the description for the FullName property specified by a Description attribute. The Manager property uses a ParenthesizePropertyName attribute so it’s surrounded by parentheses and at the top of its section. Several of the properties use the Category attribute so they appear in the Misc or Name sections. The LastNameFirst property is ReadOnly while the FirstNameLast property uses the [ReadOnly] attribute?so in the PropertyGrid they look the same. Finally, the FirstName, LastName, and FullName properties use the RefreshProperties attribute so they all get updated if any of them changes.

Serialization Attributes
The property attributes described in the previous section tell design-time editors such as the Properties window and the PropertyGrid control how to display properties. Other attribute classes do their magic at run time. One useful assortment of run-time attributes gives XML serializers information about how to serialize objects.

There’s no room in this article to do true justice to serialization but here are some basics. Serialization is the process of converting an object into a stream-like representation, often text. Deserialization is the reverse?converting a serialization back into the object it represents.

You use serialization attributes to control the “shape” of the XML the serializer produces. For example, the following listing shows the serialized XML for an array containing three OrderItem objects. The name of the file’s root element is ArrayOfOrderItem. It contains a series of OrderItem elements, each having Qty and Price attributes, and Name and Description sub-elements.

                  Cookies       One dozen cookies                 Laser Pointer       5mw Red laser pointer                 Flash Drive       256 MB flash drive        

Listing 2 shows code that makes an array of OrderItem objects, serializes them into something similar to the previous code, and then deserializes the result.

Serialization is pretty cool stuff in general, but the focus here is on the attributes applied to the OrderItem class that control the serialization. The following list describes the most useful serialization attributes.

Serializable?This attribute indicates that a class such as OrderItem is serializable. A class must provide a parameterless constructor to be serializable by the XmlSerializer class.

XmlArray?This attribute sets a name that the serializer will give to an array property.

XmlArrayItem?This sets a name that the serializer will give to the items in an array property. For example, suppose a class declares its SomeValues properties like this:

   Private m_SomeValues() As String = {"A", "B", "C"}    _    _   Public Property SomeValues() As String()      Get         Return m_SomeValues      End Get      Set(ByVal value As String())         m_SomeValues = value      End Set   End Property

Here’s how the SomeValues property serializes to XML:

        A     B     C   

XmlAttribute?This tells the serializer to serialize the property as an attribute rather than as an element. An optional string gives the name that the serialization should use for the attribute. If you omit the parameter, the serializer uses the property’s name. See the Qty and Price attributes in the earlier example.

XmlElement?This indicates that the property should be serialized as an element. An optional string sets the name the serializer will use for the element. As is the case with XmlAttribute, if you omit the parameter the serializer uses the property’s name (although if you don’t want to change the name, you probably don’t need the attribute at all).

XmlEnum?This attribute sets the text that the serializer will save for an enumerated value. For example, if an Enum defines the values Customer, Programmer, and Manager, then you can add this attribute to those values’ definitions to make the serialization save them as Cust, Pgmr, and Mgr (or even Mark, Serf, and Overlord if you like to live on the edge).

XmlIgnore?This attribute causes the serializer to skip this property completely. This is useful for properties that are derived from other values or that are determined at run time so they don’t need to be saved and restored.

XmlText?This tells the serializer to save the property’s value as text between elements rather than as a separate element.

Additional Attributes
This article describes the attributes that I’ve found most useful, but by no means is it a comprehensive list. Other attributes are available that give you more explicit control over serialization and deserialization, let you determine how the debugger steps through code, and let you declare enumerated values as bit flags. The trickiest attributes let you add support for:

?
Figure2. Smart Tags: Here’s how a smart tag and verb attribute applied to a PictureBox control show up In the designer.
  • Smart tags on the form designer (add a PictureBox to a form and look for the little box with the arrow in it).
  • Custom property displays on the Properties window (for example, a PictureBox’s Image property displays a little picture of a control’s image).
  • Custom editors on the Properties window (like the clickable editors provided by the Anchor and Dock properties).
  • Verbs on the Properties window (add a PictureBox to a form and notice the “Choose Image” link between the properties and the description below them at the bottom).

Figure 2 shows a form containing a PictureBox with the SmartTag and verb labeled.

The property-related and XML serialization attributes covered here are particularly handy when you are writing code for use by other developers. For example, if you build controls and components for others to use, these attributes can make using your components’ properties easier. Similarly, the XML serialization attributes let you control the way serializers save and restore objects. This can make serializations more readable and can sometimes save a little space in the file if you convert long names to shorter ones.

For more information, I recommend reading the online help or finding a fairly advanced book (my book Visual Basic 2005 Programmer’s Reference contains a chapter that provides more detail on using attributes). At first, attributes seem strange. Syntactically they look more like a typographer’s nightmare than a normal part of Visual Basic (or even C#). But after you get past their odd appearance?you’ll find that attributes let you add an extra dimension to your code. At a minimum, you’ll probably end up using the Description, Category, and ToolboxBitmap attributes on a regular basis. And if you’re really looking for a challenge, try writing some smart tags!

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Related Posts