RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Web Control Enhancements in ASP.NET 2.0 : Page 3

ASP.NET 2.0 does not break any existing controls from 1.1, but it adds a whole bunch of new ones, as well as several new technologies for control development.

Smart Tags
When you start using either Windows Form controls or ASP.NET Web controls in Visual Studio 2005, one of the first things you may notice is a little doohickey in the shape of an arrow in the upper right-hand corner of many controls (see the example in Figure 2). Clicking this arrow pops up a small window containing some of the control's properties and maybe a link or two. Microsoft designed these smart tags to display properties that you need to look at in order to get the control working on a page or form properly, and you'll notice that they are a bit more elaborate than just a shortcut menu. It's worth mentioning that everything I will teach you in this section applies to both Windows Form controls and ASP.NET server controls.

Figure 2: The EmailContact control's smart tag.
To build your own smart tag you need to use a control designer class. In fact, you'll use this class for a couple of other topics I'll cover later as well. But before I get into the designer class, I first need to create an ActionList class. This class will define the elements that will go into my smart tag.

An ActionList class inherits from the DesignerActionList class in the System.ComponentModel.Design namespace. But before I show you what goes into this class, let me explain that there are four types of elements that go into a smart tag: category headers, property mappings, action links, and information items. Figure 2 illustrates my goal in building this smart tag. Can you see the four types of elements I am referring to? I divided this smart tag into three categories as noted by the headings: "Appearance & Behavior," "Support," and "Information." The "Appearance & Behavior" category contains two properties: Mail Server and Pre-defined Display. These are actually properties of the EmailContact control itself. The "Support" category contains two links that activate an action of some sort, and the "Information" category simply displays information. So keeping these four types of elements in mind, I'll create my ActionList class.

I'll create a class called EmailContactActionList and derive it from DesignerActionList as I mentioned above. (You can see the complete class in Listing 1). I'll create a constructor that receives an EmailContact instance and amplifies its scope to a class-wide variable called ctlEmailContact. Later, when I add code to the designer class, you will see where and how this constructor gets used. Now I have a class-wide object that contains the instance of the Web control I am currently designing.

Next I'll create property mappings for the properties that the smart tag will display. In Figure 2 you saw that I had labeled two properties in the smart tag: Mail Server and Pre-defined Display. These will map to properties of the EmailContact control called MailServer and PreDefinedDisplay, respectively. Property mappings in an ActionList class return the control's property in the get accessor and set the control's property in the set accessor. However, due to the way Microsoft designed the ActionList infrastructure, you cannot "set" the control's properties directly. Instead, you must use reflection to access the property in the control, and then set its value. To facilitate this, I have a method called GetControlProperty that returns a PropertyDescriptor object. This eliminates the need to have to repeat the reflection code in every property mapping. Here's what one of the property mappings looks like.

   Public Property MailServer() As String
         Return ctlEmailContact.MailServer
      End Get
      Set(ByVal value As String)
         GetControlProperty("MailServer"). _
         SetValue(ctlEmailContact, value)
      End Set
   End Property
The next items I need to set up are the links that you saw in Figure 2: "About EmailContact" and a link to my Web site. These links will execute methods which I'll write into this class. My first method is called ShowAboutBox and displays a Windows Form that will serve as an About box for my control. The second method is called LaunchWebSite and performs a call to System.Diagnostics.Process.Start to launch my Web site in a browser instance. The only requirement for these two methods is that each signature must be a "Sub" (a void function in C#) and that they have no arguments.

This smart tag example displays only two properties and two links, but using the technique I just showed you, you can provide as many of both as you want. You should, however, use good judgment (I know, I keep saying that) and don't overload a smart tag with too much information. Remember, you want to put things in there that you wish to immediately make available to the page developer in order to make the Web control more intuitive.

OK, now that I have my property mappings and my action methods coded, I'm going to actually create the smart tag contents. The DesignerActionList class provides a function to override called GetSortedActionItems. Later, a designer class will override this function and it will return an object of type DesignerActionItemCollection from the System.ComponentModel.Design namespace.

The implementation of this property override will create a new DesignerActionItemCollection object (which the function will return later) and fill it with instances of four different classes: DesignerActionHeaderItem, DesignerActionPropertyItem, DesignerActionMethodItem, and DesignerActionTextItem. These are all derivatives of the abstract DesignerActionItem class. I'll walk you through these one by one.

   Dim o_Items As DesignerActionItemCollection = _
      New DesignerActionItemCollection
This code uses the DesignerActionHeaderItem class to create smart tag category headers and receive the category name in their constructor. I'll insert the instantiation of this class directly into the collection I've just created.

   o_Items.Add(New DesignerActionHeaderItem( _
      "Appearance & Behavior"))
The exact title used for each category is important in an additional way besides it being the text displayed as the category header in the smart tag. Earlier, I created two property mappings called MailServer and PreDefinedDisplay and now I'm going to add them to the smart tag. I will do this by creating instances of the DesignerActionPropertyItem class and adding them to the collection.

   o_Items.Add(New DesignerActionPropertyItem( _
      "MailServer", "Mail Server", _
      "Appearance & Behavior"))
Notice the constructor receives three arguments: the property name (as defined earlier), the caption text that will appear in the smart tag, and the exact title of the category as defined in the DesignerActionHeaderItem instantiations.

Next, I'm going to add the action links to the smart tag in the same fashion, only this time using instances of the DesignerActionMethodItem class.

   o_Items.Add(New DesignerActionMethodItem( _
      Me, "ShowAboutBox", "About EmailContact2", _
      "Support", "Displays the about box.", True))
The constructor here receives the method name, the link caption, the category text, and a description that will serve as the link's tooltip. The fourth argument in the constructor determines if this link also appears at the bottom of the property browser.

Lastly, I'll add the information item to the smart tag. I'll do this in the same fashion but using the DesignerActionTextItem class.

   o_Items.Add(New DesignerActionTextItem( _
      "ID: " & ctlEmailContact.ID, "Information"))
The constructor here receives only the text to display and the category under which to place it.

The final code in Listing 1 shows all the items I'm adding to this collection. When the method completes, it simply returns the collection. Now it's time to work on the control designer class. While I could write an article solely about the details of a designer class, I'll summarize here.

Control Designers
A control designer class inherits from System.Web.UI.Design.WebControls.CompositeControlDesigner. The class is wired to the control using an attribute at the control's class declaration.

It provides all kinds of appearance and behavior characteristics for the user's experience while in the Web Form Designer. When a page developer drops a Web control on a Web Form, various properties are obtained at different points of interaction between the page developer and the control. These properties affect what the page developer will see, starting with the control itself and including more subtle, behind-the-scenes elements, such as smart tags.

You can use smart tags to expose crucial settings of your controls to page developers.
A control designer can actually render a different output for a control during design time than the one being displayed at run time. In some cases, a control may not have any visual rendering at run time but requires some kind of display at design time in order to make it easy to work with. Examples of this are the declarative data sources that come with ASP.NET 2.0. These controls provide data storage and caching, but no visual rendering. However, in design time, they render as a grey box with some descriptive text on the web Form design space. Another example of difference in rendering can be where controls don't render anything unless they are bound to data. The GridView (or DataGrid in 1.1) looks great when it has data, but appears as nothing (or empty headers) when there are no rows to show. When you drop one onto a Web Form, you need to see what it will look like so the control renders with a few sample rows of dummy information. This is provided by the designer class.

When Visual Studio needs to determine what to display in a smart tag, it accesses a property of the designer class called ActionLists, and uses its results to build the smart tag. The ActionLists property returns an object of type DesignerActionListCollection in the System.ComponentModel.Design namespace. I will override this property in my designer class and build a DesignerActionListCollection object. The object I'll return is going to be declared at the class scope and checked against a "nothing" value in the ActionLists property.

   Private o_ActionLists As _
Values in the control designer are cached and do not need to be recreated over and over again, allowing for more efficient designer rendering for controls.

One characteristic of a control designer class is a member called Component. This is declared in the base class from which a control designer class inherits, and contains a reference to the actual control to which the designer class is wired. I can take this variable and cast it to my actual control type since the variable is declared as object.

   Dim ctlEmailContact As EmailContact = _
   CType(Component, EmailContact)
The result is an object called ctlEmailContact that contains a strongly-typed instance of the actual control this class is currently designing. Thus, any changes or actions I perform on this object will immediately be reflected on the Web Form design surface, and seen by the page developer.

The rest of the implementation of this property override includes adding an instance of the ActionList class I created earlier to the DesignerActionListCollection object I created at the class level.

   o_ActionLists.Add(New EmailContactActionList(ctlEmailContact))
Remember when I created a constructor in the EmailContactActionList class that received an instance of the EmailContact control? As you can see, I use that constructor here by passing the control instance I am designing into it.

Here's the complete source needed by the control designer class to build the smart tag:

   Private o_ActionLists As DesignerActionListCollection
   Public Overrides ReadOnly Property ActionLists() As _
         If o_ActionLists Is Nothing Then
            o_ActionLists = New DesignerActionListCollection
            Dim ctlEmailContact As EmailContact2 = _
               CType(Component, EmailContact2)
            o_ActionLists.Add( _
               New EmailContactActionList(ctlEmailContact))
         End If
         Return o_ActionLists
      End Get
   End Property
In this example, I created only one ActionList class, filled it with action list items, and added that class to the DesignerActionListCollection class to be returned by overriding the control designer's ActionLists property. I can create as many ActionList classes as I need and simply add them to the collection in the ActionLists property. This may be useful if I wanted to logically organize smart tag items in order to reuse them in multiple controls. The decision as to why and when to do this would be up to you and your requirements.

When I compile my control and drop it onto a form, I will now see a little arrow in the upper right-hand corner, and clicking it will display what you see in Figure 2. Changing any of the properties here will be the same as changing them in the property browser. Clicking on the links will perform the actions defined in the methods of the EmailContactActionList class.

That's all there is to it. Remember—don't go overboard in filling up a smart tag with properties that are not immediately needed to get the control to work. Fill them only with what you want to put in the page developer's face immediately.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date