The EmailContact Control
This last custom Web control will bring together the two previous controls and form the third type of Web control, the composite control. A composite Web control is comprised of one or more other Web controls within it. You're going to find that the code in this control is a bit clearer than in the rendered control, though I have heard argument to the contrary. A composite control can be comprised of any combination of Web controls of any type, but because it has to instantiate each of those internal controls, it takes a small performance hit that the rendered control does not take. For this reason, I typically choose to develop a Web control as a composite control when it is something I will only use just a few times at most on a Web Form.
In this case, the difference between using such a control or not is not great, since the contents of the composite control would have to be placed on a Web Form one way or another. Using this logic, it also stands to reason that the FormField control developed earlier is better performing than using individual Label, Textboxes, and/or Button Web controls multiple times on a Web Form.
The control you're going to develop is called the EmailContact control, and will display a small form that allows the user to send an e-mail. A typical use for this is a "Contact Us" page on a Web site where a visitor can send you a message right from your site. Thanks to some fancy features, the control can be reused in many fashions, including a now-famous "feedback" form found at the end of blog postings (see Figure 2). You'll also see how you're going program the e-mailing functionality directly into the control, demonstrating that custom Web controls can be more than just visual representations, but can contain certain business functionality as well.
You'll start by creating the EmailContact class and inheriting from WebControl as before; the same rule applies for inheriting from either WebControl or Control. To provide the container-styling for this control you'll use WebControl.
The Render method in a composite control initially trims down significantly from what you saw in the rendered control. I say initially because in very complex controls, the Render method can contain much more than what you're going to put in it here. Take a look at this Render method.
Protected Overrides Sub Render( _
ByVal output As HtmlTextWriter)
protected override void Render(
Notice that the second line in the method is just calling the method's base. The reason you are overriding this method is to insert the call to EnsureChildControls
before you call the base rendering method. This method checks to make sure you have set up all the child controls (this is the term I will use for the internal Web controls that reside in the composite control) appropriately before you actually render the control. Before I show you how to set up child controls, let's determine what child controls are needed and declare them at the top of the class.
The visual elements that are going to be needed for this control consist of a sender's name, sender's e-mail, a recipient's e-mail, an e-mail subject, e-mail body, and of course a send-button. You're also going to add a heading to the top that may come in handy somewhere. The five fields I've just described are going to require a caption and a textbox for each. Now where do you suppose you can get some of those? That's rightyour EmailContact control will contain five instances of the FormField control. It will also contain a Label for the heading and a ConfirmationButton for the send-button. You might as well leverage the button created earlier and gain some of its functionality as well. For this reason, don't forget to include the assemblies of the previous two controls in your references. You declare child controls at the top of the class like this:
Private lblHeading As Label = New Label
Private ctlFromName As FormField = New FormField
Private ctlFromEmail As FormField = New FormField
Private ctlToEmail As FormField = New FormField
Private ctlSubject As FormField = New FormField
Private ctlBody As FormField = New FormField
Private btnSend As ConfirmationButton = _
private Label lblHeading = new Label();
private FormField ctlFromName = new FormField();
private FormField ctlFromEmail = new FormField();
private FormField ctlToEmail = new FormField();
private FormField ctlSubject = new FormField();
private FormField ctlBody = new FormField();
private ConfirmationButton btnSend =
Now that you've established the child controls needed and declared them, you need to massage their properties appropriately and make them visually part of the control. This is done by overriding the CreateChildControls
method. This method gets called within the page lifecycle and is where you are expected to build your control tree. Our EmailContact class inherits a property called Controls
which is of type ControlsCollection. This property is what contains all the child controls as well as any literal HTML text you want to render as well. Later, when you hit the Render
method, the Controls
collection will render all its contents in the order they were placed in it. I called this a control tree for a reason. It's worth mentioning that you can create custom composite controls that contain other custom composite controls, thus creating a control tree. Remember however, that the deeper you get the more performance-heavy your control will get. Let's take a look at a simple example of the CreateChildControls
method using the lblHeading
, and btnSend
child controls only, in the interest of space.
shows the code in Visual Basic. Listing 2
shows the same code in C#.
Let's go through the code in this method because it is the meat of this control and where most of the work will be done. The first part of the code simply sets up the properties of your child controls. Notice that there is a lot of property setting going on here. For example, the CaptionWidth
property if the ctlSubject
control is set to the CaptionWidth
property of your EmailContact control; this is called mapping. Because this custom control contains other controls, the properties of those contained controls are not automatically exposed to the outside, nor do you want them to be. To solve this you'll add a CaptionWidth
property to your control, just like you did to the FormField control before. The property of the child control(s) is then set to the one of its container, which is your composite control. The effect here is that when the programmer uses this on a Web Form and sets the CaptionWidth
property in the property browser or in the code-behind class, the child controls that need to will also receive the appropriate setting. This same technique is repeated with other properties.
The second part of the CreateChildControls
method builds the Controls collection using the child controls and another control called LiteralControl, which as you can guess is used to place literal text into the Controls collection. This text almost always represents HTML that you want to code around your child controls. You're once again building HTML, just in a different manner. Notice that some of the building is dependent on the settings of some properties. The point is that when you use the control on a Web Form, turning a property like ShowSubject
off can toggle the visibility of that portion of the control, making the control much more versatile and reusable. For example, you can choose to display the "To Email" field and allow the user to enter a destination e-mail, thus using this control to send e-mail to anyone. In another case, such as a "Contact Us" page, you can turn the "To Email" field off, yet set a property that the control will also have called ToEmail
. This property, which would have to otherwise be set by the "To Email" field, would be used to send the e-mail out without giving the user a choice. In this case, the ToEmail
property could be set to your tech-support e-mail address. (See Figure 3
as an example.)
|Figure 3 Yet Another Style: You might use this style for the EmailContact control on a site to provide tech-support contact functionality.|
There's one other thing I want to point out in this code. The Width
property of our Label and our FormField control is set to 100%; as is the table cells that contain them, as well as the surrounding table. The point here is that you size your child controls to the full width of the container. Therefore, when you drop the control on a Web Form and resize the control, the contents get resized right along with it. There are of course exceptions depending on what child controls you're talking about-notice the Send button does not receive the same treatment.
Also notice that I set the ID
property of each of the child controls. This is especially important for the purpose of state maintenance. Your child controls know how to maintain state for themselves without any problem, but the ViewState variable uses the ID
of each control to save and retrieve its state. Remember in the FormField control you set the ID
attributes of the three tags according to the UniqueID
of the custom control. By setting the ID
of the child controls, you are effectively setting their UniqueID
, thus giving each control the information it needs to name its internal tags and thus maintain state properly.
This leads me to another addition you must do. Your composite control must implement an interface called INamingContainer. This is an identification interface only and contains no implementation. It is meant to instruct the page parser to appropriately name all child controls upon rendering. You recall that in the FormField control you set the ID and Name attributes to the UniqueID of the control and a hard-coded tag name, delimited by an underscore character (_) for ID and a colon (:) for Name. As I stated then, this is the naming convention that you have to follow for rendered controls. Composite controls have the capability to adhere to this naming standard automatically so long as you implement the INamingContainer interface. Without doing this, you run the risk that the user of your custom control will name it on a form using an identical name of a contained child control, which would cause a problem for the page's state maintenance mechanism.