The following code fragment applies the Description, ReadOnly, and DefaultValueattributes to the FirstNameproperty: _Public Property FirstName As String.... Browsable This attribute det
Trang 1Attributes and XML
Comments
The Properties window and IntelliSense do a remarkable job of figuring out how your objectsand their properties work For example, suppose your program uses the following code to definethe JobTypesenumerated values and the AssignJobsubroutine:
Public Enum JobTypesDesign
DocumentationDevelopmentTestingDebuggingEnd EnumPrivate Sub AssignJob(ByVal job_type As JobTypes)
‘
End Sub
If you type “AssignJob(“ in the code window, IntelliSense automatically displays all of the
infor-mation shown in Figure 12-1 It shows the AssignJobsubroutine’s signature below the cursor, itdisplays the list of possible JobTypesvalues that you could use for the subroutine’s first parame-ter, and it even displays the numeric value of the currently selected JobTypesvalue
Figure 12-1: IntelliSense automatically displays a subroutine’s signature and lists possible JobTypesvalues
Trang 2Similarly, the Properties window automatically discovers the types of the properties provided by a ponent, and supplies appropriate editors in many cases For example, it automatically provides drop-downs for selecting colors or enumerated values, dialogs for selecting files or colors, and a combination
com-of sub-properties and a dialog for selecting fonts
Visual Studio provides all of this support automatically
Although all of these tools are extremely useful, Visual Studio cannot deduce the intent of your code.IntelliSense can list the signature of the AssignJobssubroutine, but it cannot guess the routine’s pur-pose It can display a list of the possible values that you might use for the routine’s parameter, but itdoesn’t know what the parameter is for
By using attributes and XML comments, you can give additional information that IntelliSense and editorssuch as the Properties window can use to provide extra support for developers In most cases, adding thissupport takes only a few seconds, but giving developers a better understanding of what the code is sup-posed to do can save you hours of frustrating debugging and maintenance
This chapter is divided into two parts that describe some useful attributes and XML comments (You candownload examples demonstrating most of these attributes and XML comments at www.vb-helper.com/one_on_one.htm.)
Attributes
Visual Studio uses attributes to “decorate” code with extra information This information is mostly used bycode generators, editors, and other processes that manipulate the code itself For example, the Propertieswindow uses many attributes to decide how a particular item should be displayed and edited
Run-time code can also examine attributes to learn more about a particular item For example, XMLserialization routines can examine an object’s attributes to learn more about how the object should
be serialized
In fact, you can make your code examine an object’s attributes This is a relatively unusual need, ever, so it’s not described here For more information, search the online help for “Retrieving Information Stored in Attributes,” or look at msdn.microsoft.com/library/en-us/cpguide/html/
how-cpconretrievinginformationstoredinattributes.asp.
Attributes can decorate many different kinds of programming entities, including modules, classes, tures, subroutines, functions, properties, fields, enumerated types, and method parameters You can onlyapply an attribute to an item for which it was designed
struc-For example, the Browsableattribute determines whether a property or event should be visible in theProperties window You can apply the Browsableattribute to a class, but it will be ignored
To apply an attribute, you enclose a call to the attribute class’s constructor inside pointy brackets beforethe code item The following code fragment shows how you might add a Descriptionattribute to aclass’s FirstNameproperty:
Trang 3Imports System.ComponentModel
<Description(“The employee’s first or given name”)> _Public Property FirstName() As String
End PropertyThe Descriptionattribute class is contained in the System.ComponentModelnamespace so the codeimports System.ComponentModelto make using the attribute easier The call to the Descriptionattribute’s constructor takes a single parameter giving the property’s description The call is enclosed
in pointy brackets
The attribute is followed by a line-continuation character, so the attribute is actually part of the same line
of code as the property’s declaration Putting attributes on separate lines makes the code easier to read
The class name for an attribute generally ends with the word “Attribute.” In Visual Basic, you canomit this last part for convenience and to make the code less cluttered For example, the class that pro-vides the Descriptionattribute is named DescriptionAttribute Normally, you don’t need toworry about this, but if you want to learn more about the class, you should search the online help for
“DescriptionAttribute class.”
You can include multiple attributes inside the pointy brackets to apply more than one attribute to a codeitem The following code fragment applies the Description, ReadOnly, and DefaultValueattributes
to the FirstNameproperty:
<Description(“The employee’s first or given name”), _[ReadOnly](True), _
DefaultValue(“(missing)”)> _Public Property FirstName() As String
Notice square brackets around the ReadOnlyattribute’s name This lets Visual Basic distinguish betweenthe attribute name and Visual Basic’s ReadOnlykeyword
An alternative technique is to include each attribute within its own set of pointy brackets, as shown inthe following code I prefer this version because it is a little easier to read, you don’t need to worry aboutmatching up the commas properly, and you can work with each attribute separately For example, youcould delete the line containing the DefaultValueattribute, or add a new attribute line without modi-fying the others
<Description(“The employee’s first or given name”)> _
<[ReadOnly](True)> _
<DefaultValue(“(missing)”)> _Public Property FirstName() As String
The NET Framework defines more than 300 attribute classes, so they are not all described here Manyare useful only under very specific circumstances, so there are many that you will probably never need
to use
Trang 4The following sections described some of the attribute classes that I have found the most useful, grouped
by the types of tasks they perform They describe the attributes’ purposes and the code items that aremostly likely to use the attributes They also include a brief example
Helping the Properties Window
These attributes help the Properties window do a better job They tell the window how to display andedit properties and events
AmbientValue
This attribute indicates that a property gets its value from another source unless it is overridden Forexample, controls typically use the Font, ForeColor, and BackColorproperties used by their parentcontrols unless you override them
The following code makes the BoxColorproperty ambient:
End GetSet(ByVal value As Color)m_BoxColor = valueEnd Set
End Property
The property get procedure checks the m_BoxColorvariable If no value has been assigned to the variable,and if the control has a parent, then the procedure returns the parent’s ForeColorproperty
Browsable
This attribute determines whether a property or event is visible in the Properties window This is useful
if you want to make a property or event available at run-time but not at design time For example, pose you build a component with a CurrentPriceproperty that uses a Web Service to get a stock price
sup-at run-time It might not make sense to display the stock price sup-at design time
The following code fragment hides the CurrentPriceproperty from the Properties window:
<Browsable(False)> _
Public Property CurrentPrice() As Single
Category
The Categoryproperty determines the category in which a property or event is grouped in the
Properties window when you click the window’s Categorized button You don’t need to do anythingelse to define a new category Simply type a new category name into a Categoryattribute and theProperties window will make a new category for it
Trang 5<Category(“Name Values”)> _Public Property FirstName() As String
DefaultProperty
This attribute indicates a component’s default property When you select a control in the form designer,the Properties window initially selects the same property that it already had selected For example, if youhave the Textproperty of a Labelselected and then click a TextBox, the Textproperty is still selected
If the newly selected control doesn’t have the selected property, the Properties window selects the trol’s default property
con-The following code sets the UserControl1class’s default property to DrawingFlags:
<DefaultProperty(“DrawingFlags”)> _Public Class UserControl1
Private Const DEFAULT_TITLE As String = “Untitled”
Private m_Title As String = DEFAULT_TITLE
<DefaultValue(DEFAULT_TITLE)> _Public Property Title() As String
Using the DefaultValueattribute is straightforward for simple data types such as strings and singles.For properties that are objects, you must use a different attribute constructor that includes the object’stype and a string value that can be converted into that type
Usually, you should reset object properties to Nothing The following code shows how to do this for anImageproperty:
<DefaultValue(GetType(Image), Nothing)> _Public Property StatusImage() As Image
Description
The Descriptionattribute sets the text displayed below the Properties window when the property isselected The following code places the FirstNameproperty in the “Name Values”category and givesthe property a description
Trang 6<Category(“Name Values”)> _
<Description(“The employee’s first or given name.”)> _
Public Property FirstName() As String
The following code makes the Properties window display the RuntimeUserproperty with the nameCustomer:
<DisplayName(“Customer”)> _Public Property RuntimeUser() As Person
Trang 7This attribute associates a property with an editor class that can edit the property See the section
“Displaying and Editing LineWidth” in Chapter 11 for more information about this attribute
ReadOnly
This attribute determines whether the developer can modify a property in the Properties window Thefollowing code allows the developer to see the OutsideTemperatureproperty but not modify it:
<[ReadOnly](True)> _Public Property OutsideTemperature() As Single
Figure 12-3 shows the Properties window displaying this property The property’s name and value aregrayed out and the value is not editable
Figure 12-3: The ReadOnlyattribute makes the Propertieswindow display a property grayed out and not editable
Localizable
This attribute determines whether a property’s value is saved in localized resource files If this is False(the default), the property value is saved only once and is shared for all locales If this is True, thenwhen you design for different locales in the form designer, the property’s localized values are saved intothe appropriate resource files
The following code makes the ReportLanguageproperty localizable:
<Localizable(True)> _Public Property ReportLanguage() As String
NotifyParentPropertyAttribute
This attribute determines whether a parent property is notified when this child property is changed Forexample, if a property is a Structurethat contains other properties, you can use this attribute on theStructure’s properties
Trang 8The following code makes the FirstNameproperty notify a parent property when it is changed:
at the top, so this can make key properties easier to find When the Properties window is grouping items
by category, the parenthesized properties appear at the top of their category
The following code makes the Properties window display an Aboutproperty with a parenthesizedname This is a dummy property that doesn’t store or display any value However, it has an associatededitor class named AboutEditor That makes the Properties window display an ellipsis to the right ofthe property’s value If you click the ellipsis, the AboutEditordisplays a simple Aboutdialog
‘ A dummy property to display an About dialog
<ParenthesizePropertyName(True)> _
<Editor(GetType(AboutEditor), GetType(UITypeEditor))> _
Public Property About() As String
GetReturn NothingEnd Get
Set(ByVal value As String)End Set
End Property
Download the UseAttributesexample program and look at the code to see exactly how the AboutEditorclass works See the section “Displaying and Editing PolyPolyline” in Chapter 11 for more information onproperty editors
You can see the DatabasePasswordproperty in Figures 12-2 and 12-3
Note that the property’s value is still stored in plain text in the designer-generated code, so the valueisn’t completely hidden It just doesn’t jump out for people walking past the developer to see
PropertyTab
This attribute associates a component class with a property tab class The property tab class allowsthe Properties window to display an additional tab that can provide a customized view of the compo-nent’s properties
Trang 9Figure 12-4 shows the Properties window displaying a tab named Test Properties This tab displays onlyproperties that are in the Test Properties category.
Figure 12-4: The Test Properties tab displays only properties in the Test Properties category
The following code shows how the program associates the UserControl1class with theTestPropertiesTabclass:
<PropertyTabAttribute(GetType(TestPropertiesTab), PropertyTabScope.Document)> _Public Class UserControl1
The following code shows how the TestPropertiesTabclass works:
Imports System.ComponentModel
‘ This tab lists only properties in the Test Properties category
Public Class TestPropertiesTabInherits PropertyTab
‘ Returns only the properties in the Test Properties category
Public Overloads Overrides Function GetProperties(ByVal component As Object, _ByVal attributes() As System.Attribute) _
As System.ComponentModel.PropertyDescriptorCollectionDim props_in As PropertyDescriptorCollection
If attributes Is Nothing Thenprops_in = TypeDescriptor.GetProperties(component)Else
props_in = TypeDescriptor.GetProperties(component, attributes)End If
Dim props_out As New PropertyDescriptorCollection(Nothing)Dim i As Integer
For i = 0 To props_in.Count - 1
If props_in(i).Category = “Test Properties” Then
‘ Create a PropertyDescriptor for this property
props_out.Add( _
Trang 10TypeDescriptor.CreateProperty( _
props_in(i).ComponentType, _props_in(i), _
Nothing) _)
End IfNext iReturn props_outEnd Function
Public Overloads Overrides Function GetProperties(ByVal component As Object) _
As System.ComponentModel.PropertyDescriptorCollectionReturn Me.GetProperties(component, Nothing)End Function
‘ Provides the name for the property tab
Public Overrides ReadOnly Property TabName() As StringGet
Return “Test Properties”
End GetEnd Property
‘ Provides an image for the property tab
Public Overrides ReadOnly Property Bitmap() As System.Drawing.BitmapGet
Return My.Resources.TestPropertiesEnd Get
End PropertyEnd Class
The parent class PropertyTabdoes most of the work
The class overrides its inherited GetPropertiesmethod to return a collection of objects describing theproperties that should be displayed on the tab The routine loops through the components and addsthose in the Test Properties category to the result collection
The tab class’s GetPropertiesfunction calls its GetPropertiesmethod and returns the result
The read-only TabNameproperty returns the name of the tab This is the text displayed in the tooltipshown in Figure 12-4
Finally, the read-only Bitmapproperty returns the bitmap displayed for the tab in the Properties window
In this example, the property returns the Bitmapstored in the TestPropertiesresource (You can load this example at www.vb-helper.com/one_on_one.htm.)
down-RefreshPropertiesAttribute
This attribute determines how the Properties window refreshes its display when the property’s valuechanges The ContactViewercontrol described in Chapter 11 uses this attribute to update its propertydisplay The control has a property of type Contact The Contactclass has FirstName, LastName,andPhoneproperties The program uses a type converter to allow the Properties window to display
Trang 11these sub-properties It also flags the FirstName, LastName, and Phoneproperties with the RefreshPropertiesAttributeto make the Properties window update the Contactproperty’s display when-ever one of its sub-properties changes.
The following code shows how the example applies this attribute to the FirstNameproperty:
<RefreshProperties(RefreshProperties.Repaint)> _Public Property FirstName() As String
<TypeConverter(GetType(PolyPolylineConverter))> _Public Property PolyPolyline() As PolyPolyline
See the section “Displaying and Editing PolyPolyline” in Chapter 11 for more information on thisattribute
Helping the Form Designer
The attributes described in the following sections provide extra help for the form designer
Docking
This attribute indicates whether a control should automatically dock to fill its container if it is created in
a container that holds no other controls
The following code makes the UserControl1control dock to fill its container:
<Docking(DockingBehavior.AutoDock)> _Public Class UserControl1
Trang 12
This attribute sets the bitmap displayed for a component in the toolbox The following code sets the box bitmap for the UserControl1control to the tbxUserControl1resource in the assembly containingthe UserControl1class:
This attribute determines the type of toolbox item displayed for a component, or whether a toolbox item
is displayed at all This is useful, for example, if you want to build a constituent control for use in anothercontrol, but you don’t want to make the constituent control directly available to other developers
The following code makes the ToolNotInToolboxcomponent not appear in the toolbox:
<ToolboxItem(False)> _
Public Class ToolNotInToolbox
Helping the Code Editor
The attributes described in these sections help the code editor They provide additional informationabout items so that the code editor can display them, and provide help appropriately at design time andwhile debugging
infor-Conditional
This attribute can make the run-time system ignore calls to a method when the program executes Thefollowing code demonstrates the Conditionalattribute:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles MyBase.Load
Trang 13IfCondition1()IfCondition2()End Sub
<Conditional(“DEBUG”)> _Private Sub IfCondition1()MessageBox.Show(“IfCondition1”)End Sub
<Conditional(“Condition2”)> _Private Sub IfCondition2()MessageBox.Show(“IfCondition2”)End Sub
End ClassThe code defines two conditional subroutines: IfCondition1and IfCondition2 IfCondition1isconditional on the DEBUGcompilation constant, so it executes only if the program is running in debugmode
IfCondition2is conditional on the Condition2compilation constant To set this constant, click My Properties, select the Compile tab, and click the Advanced Compile Options button to displaythe dialog shown in Figure 12-5 Enter the constant’s definition in the “Custom constants” text box
double-Figure 12-5: You can use the Advanced Compiler Settings dialog to define compilation constants
If a conditional method’s condition is not defined as True, the method is compiled into the executableprogram, but calls to it are ignored at run-time
Trang 14This attribute determines when a property is visible to IntelliSense You can set this to Always(appears
in all IntelliSense tabs), Advanced(appears in the All tab, but not the Common tab), or Never(doesn’tappear in either IntelliSense tab) The following code makes the RuntimeUserproperty appear inIntelliSense’s All tab:
<EditorBrowsable(EditorBrowsableState.Advanced)> _
Public Property RuntimeUser() As Person
Flags
This attribute indicates that an enumerated type should be treated as a bit field that allows combinations
of its values To allow developers to make bitwise combinations of values, you should set the values topowers of 2: 1, 2, 4, 8, 16, and so forth You should also create a Nonevalue set to 0 If you like, you candefine common combinations of values
The following code defines values that tell a control whether to draw a box, X, or cross The valueSpidermeans both an X and a cross
<Flags()> _
Public Enum DrawingFlagValues
None = 0Box = 1
X = 2Cross = 4Spider = X Or CrossEnd Enum
Trang 15This attribute tells IntelliSense to hide a module’s name You can still type the module’s name if youlike, and IntelliSense will show the items inside the module The following code hides the name ofHiddenModulefrom IntelliSense:
<HideModuleName()> _Module HiddenModule
ProvideProperty
This attribute gives the name of a property that an extender provider implements for its client controls.The following code indicates that the ExtraTagProviderclass provides the ExtraTagproperty:
<ProvideProperty(“ExtraTag”, GetType(Control))> _Public Class ExtraTagProvider
For more information on building extender providers, see the section “Building Extender Providers” inChapter 10
Obsolete
This attribute indicates that an item is obsolete Optionally, you can provide a message for Visual Studio
to display in the Error List window and as a tooltip when the developer hovers the mouse over the codethat uses the obsolete item
The following code marks the SortCustomerArraysubroutine as obsolete If the code uses this tine, Visual Studio displays the warning, “This method has been replaced with SortArray,” as shown inFigure 12-6
subrou-<Obsolete(“This method has been replaced with SortArray”)> _Public Sub SortCustomerArray(ByVal customer_array() As Customer)MessageBox.Show(“SortCustomerArray”)
End Sub
Figure 12-6: The code editor displays an error message if you use an obsolete subroutine
This attribute can also indicate whether using the obsolete item should generate a warning or an error Forexample, when you replace a subroutine in a new release of your software, you can make calls to the oldversion raise a warning so developers can find and fix them In the next release of your code, you can makethe old call cause an error so that developers who have not yet updated their code are forced to do so
This attribute can also be a useful tool in managing your own code If you want to replace a routine with
a better version, you can mark the old version as obsolete That gives you a little extra documentation onthe routine, and Visual Studio will nag you about using the routine until you get around to removing it
Trang 16Helping Serialization
The attributes described in these sections help determine how objects are serialized and deserialized.These are a bit different from the attributes described so far, because they are used at run-time, whereasthe previously described attributes are used by the Properties window, form designer, code editor, andother design-time tools
The reason Visual Basic uses attributes here instead of some more run-time–oriented technique is thatserializers use reflection at run-time to examine the objects they are serializing Attributes decorate aclass, property, or other item so that the serializer can learn about the attributes at run-time
OnDeserializing, OnDeserialized, OnSerializing, and OnSerialized
These attributes indicate that a class method should be executed during and after serialization ordeserialization
The following code fragment shows part of the Personclass The OnSerializingMethodsubroutinehas the OnSerializingattribute, so it is called when a Personobject is serialized In this example,the subroutine sets the Personobject’s FirstNameproperty to “OnSerializingMethod”
<Serializable()> _
Public Class Person
<OnSerializing()> _Friend Sub OnSerializingMethod(ByVal context As StreamingContext)FirstName = “OnSerializingMethod”
End Sub