Creating custom server controls with ASP.NET is fairly straightforward. A custom ASP.NET server control is any class that inherits from the Control class of the System.Web.UI namespace, or one that inherits from any of the controls derived from Control. Choosing the control from which to derive your custom control is largely a matter of how much functionality you want to build into your control. You’ll learn how to choose a control to inherit from later in this section.
All server controls that send output directly to the browser do so by overriding the Render method of the control from which they’re derived. Controls derived from a control with its own UI rendering (such as the TextBox server control) should also call the Render method on the base class in order to have the base control render its own output. Note that the location in your code at which you call the Render method of the base class is important. That’s where the rendered output of the base control will appear. The Render method of the sample file TextBoxPlus.cs, included with the sample code for this chapter, demonstrates this concept.
In addition to sending output to the browser directly through the Render method, some custom controls (compositional controls in particular) send output to the browser indirectly by overriding the CreateChildControls method of the base Control class, which is used to add controls to a custom control at runtime. Some custom controls also call the RenderChildren method, which causes all of the child controls of a given control to execute their Render methods.
While there is no inherent difference in the resulting HTML on the browser between rendering controls using the Render method and composing controls using the CreateChildControls method, there are differences in terms of performance and ease of programming. Using composition is somewhat easier for the developer, and it does not require any knowledge of HTML. Rendering offers better performance and reduced overhead, but to take advantage of it you do need to know HTML. Additionally, if you want to create a control that contains two distinct server controls (such as a text box with a built-in validation control), a composite control is really the only way to go.
Each custom control must belong to a namespace. The @ Register directive, which makes custom controls available for use in a page, has a Namespace attribute that is used to locate the control class to instantiate at runtime.
You create a namespace using the namespace keyword. In C#, you use the namespace keyword and a bracketed block of code following it to define the scope of the namespace.
namespace myN // Class definitions, etc. go inside the namespac }
Note that if you are creating your custom control using Microsoft Visual Studio .NET, the IDE will create the namespace for you automatically.
Although Visual Studio .NET creates a namespace for each new project automatically, this is implemented differently depending on the language you choose. For example, in a Microsoft Visual C# project, the namespace keyword is added to class files and code-behind files automatically.
In a Visual Basic .NET Web application, the namespace for the project is defined by the Root Namespace option set in the Project Properties dialog box for the project. This means that although you will not actually see the Namespace keyword in your class files, a namespace will be created when the class is compiled during the build process for the project.
In both languages, the default name for the namespace is the name of the project.
Each custom server control is defined in a class. You can have more than one class defined in a single namespace within a file, but the class is what defines the boundary of the control. You can put multiple controls within a single file, if you want. When you compile this file, all of the classes (and the controls they represent) are compiled into a single assembly. When you have multiple related controls, having them all in a single assembly can make deploying your controls simpler.
Similar to namespaces, classes in Visual C# .NET are defined with the class keyword and a pair of curly braces that together define the scope of the class.
class myContro // Variables, procedures, etc }
As with namespaces, Visual Studio .NET automatically takes care of creating a class for you when you create a new Web Control Library project. This default class, which inherits from the WebControl class in the System.Web.UI.WebControls namespace, contains a property declaration for a default Text property, as well as an overridden Render method that renders the contents of the Text property to the browser. These default members are provided to give you a head start in creating your own control functionality.
The Standard Edition of Visual C# .NET does not support the Web Control Library project type. You can, however, still add a Web Custom Control to a Web Project. If you want to create a standalone assembly for your custom control for reuse in other projects, you can simply remove the default Web Form (WebForm1.aspx) from the Web Project, and then add one or more Web Custom Control classes to the project for the control or controls you want to create.
One of the most important steps in creating a custom server control is choosing the class from which your control will inherit. This can be a more difficult decision than you might think, because you have so many choices.
You need to consider both how the control will be used and who will use it. Since one of the reasons for creating custom controls in the first place is to reuse controls that have most (but not quite all) of the functionality you need, you might be tempted to simply jump right into inheriting a very rich control to get the most bang for your buck. But keep in mind that any public property, method, or event exposed by the base control will be exposed by a derived control automatically. If you don’t plan for this, your control might do something unexpected, or you might unwittingly introduce bugs when someone uses the control in a way that you didn’t anticipate.
This might not be a problem if you are creating the control for your own use. If you plan to share it with others, however, just remember that people can be very creative in finding ways to break software, and developers are more creative than most. By making available a lot of public methods that you didn’t write, you increase the chances that another developer will break your code. To avoid this problem, you should generally inherit from the class that provides the fewest public members while still providing the desired functionality.
For example, let’s say you want basic UI plumbing code but don’t want the additional functionality of a TextBox or Label control. You can inherit from the WebControl class, which implements such basic UI features as the BackColor and ForeColor properties and the ApplyStyle method. As noted earlier, this is the default for a Web Custom Control class added with Visual Studio .NET. If you don’t even need the UI plumbing, you can inherit from the Control class, which has no UI-related members. Of course, this means that you have to implement all UI-related code yourself.
Another important consideration in the implementation of a control is rendering its output. To do this, you override the Render method of the base class. Remember that, as when you choose a base class to inherit from, this step is handled for you automatically when you add a Web Custom Control class to a Visual Studio .NET project.
protected override void Render(HtmlTextWriter output // custom rendering cod }
As mentioned earlier, you can also call the Render method of the base class in order to send its rendered output to the browser. This is useful mainly for controls that inherit from classes that define their own UI, such as the TextBox class. Note that the call to the Render method of the base class can appear anywhere within the Render method of the derived control, allowing you to determine where in your custom control the rendered output of the base class will appear. The MyBase keyword in the following snippet provides access to the members of the class from which the current class is inherited:
protected override void Render(HtmlTextWriter output // custom rendering cod base.Render(output) // additional custom rendering code, if desire }
Note that since the ASP.NET runtime calls the overridden Render method automatically when the containing page is requested, passing in the necessary HtmlTextWriter instance (the output variable in the previous code snippet), you can simply pass that instance as the argument to the Render method in the base class.
Once you understand the basics of writing a control, the next step is to actually write one. There are several ways to create a control. Of course, you can use the tools provided with the .NET SDK and create the control in Notepad or a similar editor, or you can use Visual Studio .NET. Depending upon the version of Visual Studio .NET you use, you can create your controls two ways. Using the Professional version or greater, you can create a separate Web Control Library project to house your Web control. Using the Standard or Deluxe Learning Edition, you can create Web controls only as part of an ASP.NET Web Application project. In the examples that follow, we’ll build the controls using the former method.
In the first example, you’ll create a control that adds a built-in label to a simple TextBox control.
Open Visual Studio .NET and create a new Web Control Library project. Name the project Chapter_10_Controls. When project creation is complete, delete the default class, WebCustomControl1.cs.
Right-click the project in Solution Explorer, select Add, and then select Add Component. The dialog box that appears has quite a few likely sounding options (including Component Class). Select the Web Custom Control entry toward the bottom of the list of options. Name the control TextBoxPlus.cs, as shown in the following illustration.
The default class added by Visual Studio .NET is a completely functional (though otherwise uninteresting) example control. It inherits from the WebControl class, and it contains a string property named Text. The Render method simply writes the Text property to the HTML stream being written. Once compiled, the control will reside in the Chapter_10_Controls namespace. Visual Studio .NET also automatically adds metadata attributes to the class to provide design- time support for the control. You’ll learn about these attributes later in this chapter.
Begin customizing the control by changing the class that the control inherits from to the TextBox class (which also resides in the System.Web.UI.WebControls namespace) in order to reuse its functionality. Change the Inherits statement that immediately follows the class declaration from
public class TextBoxPlus : System.Web.UI.WebControls.WebControl
public class TextBoxPlus : System.Web.UI.WebControls.Textbox
Change all occurrences of Text (the property definition) to LabelText, and all occurrences of text (the name of the private instance variable that contains the value of the property) to _labelText. The LabelText property will allow users of the control to set the text to be displayed with the text box.
Add a call to render the base control. Within the Render method, add the following line immediately after the call to output.Write:
This will cause the output of the base TextBox control to be rendered immediately following the text of the LabelText property. The complete code for the class is shown here. (The attributes have been removed for clarity.)
public class TextBoxPlus : System.Web.UI.WebControls.TextBo private string _labelText [Bindable(true), Category("Appearance"), DefaultValue("") public string LabelText ge return _labelText se _labelText = value protected override void Render(HtmlTextWriter output output.Write(LabelText) base.Render(output) }
Save the class file, and build the project. The control is now ready for use.
The next step is to actually use the control. To make using the control as easy as possible, you can add your custom control to the Visual Studio .NET Toolbox, as described in the following steps.
Without closing the Chapter_10_Controls project, from the File menu, select Add Project then New Project. Choose the ASP.NET Web Application type, and name the project Chapter_10. The IDE should automatically load the WebForm1.aspx file of the new project.
Open the Toolbox, right-click inside the Toolbox, and then select Add/Remove Items from the drop-down menu. The dialog box shown in the following illustration will appear.
Click the Browse button. In the resulting dialog box, navigate to the bin folder under the Chapter_10_Controls project folder, select Chapter_10_Controls.dll, and then click OK. You will be returned to the previous dialog box with the TextBoxPlus control highlighted, as shown in the following illustration.
Click OK to close the Customize Toolbox dialog box. The TextBoxPlus control will be added to the Web Forms tab of the Toolbox (or whichever tab was active when you selected Customize Toolbox).
Of course, adding the control to the toolbox is only part of the process. Next you’ll learn how to use the custom control on a Web Forms page.
Use the Properties window to change the pageLayout property of the WebForm1.aspx to FlowLayout. (Because of the way the TextBoxPlus control is rendered, it will not display correctly if the page is in GridLayout mode.)
Open the Toolbox if it’s not already open, select the Web Forms tab, and then scroll to the bottom of the tab. You should see the TextBoxPlus control at the bottom of the list, as shown in the illustration on the next page.
Double-click the TextBoxPlus control. This will add a copy of the control to WebForm1.aspx. Select the control, and then use the Properties window to change the LabelText property to Name:. The page with the control should look like the following illustration.
Right-click WebForm1.aspx, select Browse With, and then select Microsoft Internet Explorer. The output should look similar to the following illustration.
While Visual Studio .NET makes it easy to add a control to a page, there is no magic involved. All of the things that Visual Studio .NET does while adding a component could be done without Visual Studio .NET. It is important to understand some of the things that Visual Studio .NET is doing. When you add a control by double-clicking the control or by dragging it from the toolbox to the page, an @ Register directive is added at the top of the .aspx file, just below the @ Page directive. In this example, the @ Register directive looks like the following line:
<%@ Register TagPrefix="cc1" Namespace="Chapter_10_Controls Assembly="Chapter_10_Controls" %>
Additionally, the tag to create the control is added wherever the control has been dropped (or at the cursor location, in the case of double-clicking the control in the Toolbox):
<cc1:TextBoxPlus id="TextBoxPlus1" runat="server LabelText="Name:"></cc1:TextBoxPlus>
The default TagPrefix used by Visual Studio .NET is cc1, cc2 and so on. You can manually change the TagPrefix and then change the beginning namespace part of the opening and closing tag from cc1: to whatever you would like to use. Alternatively, you can use the TagPrefix attribute in the control to specify a TagPrefix to be used when the control is dropped onto a form in Visual Studio .NET. Add the following line to the AssemblyInfo.cs file in the control project,
[assembly: TagPrefix("Chapter_10_Controls","MyControls ")]
and then rebuild the project. You might have to remove the custom control from the toolbox and add it again as described earlier for the changes to take effect in the Toolbox.
In addition to adding the control to the page declaratively as shown earlier, you can add custom controls to the page programmatically, just as you can with the standard ASP.NET server controls. Adding controls programmatically does not require the @ Register directive. If you created the control in a separate project from the project in which it will be used, you might want to add a using directive to the codebehind class to avoid the need to use the fully qualified name to refer to the controls in your assembly.
Open the Chapter_10_Controls solution you created earlier if it’s not already open. The solution should contain both the Chapter_10_Controls and Chapter_10 projects. If not, add the missing project to the solution by right-clicking the solution name, selecting Add and then Existing Project.
Add a new Web Form to the Chapter_10 project. Name the form AddControl.aspx, and change its pageLayout property to FlowLayout.
Double-click anywhere on the page and you will be brought to the Page_Load event handler in the code-behind module for AddControl.aspx. Add the following code to the event handler:
Chapter_10_Controls.TextBoxPlus TBP = new Chapter_10_Controls.TextBoxPlus() TBP.LabelText = "Programmatic Control Label: " this.Controls.Add(TBP);
Save the changes, and then build the project. Browse AddControl.aspx.
The result will be an error informing you that “Control ‘_ctl0’ of type ‘TextBoxPlus’ must be placed inside a form tag with runat=server.” The reason for this error is that the Add method of the Controls collection actually places the added control at the end of the controls collection. In this case, that means the control is placed after the last HTML tag in the page (which is represented in the Controls collection as an ASP.NET Literal control). Because the TextBox control from which TextBoxPlus is derived must be placed between the <form runat="server"> and </form> tags, this code generates an exception. There are a couple of solutions for this. You could use the AddAt method rather than the Add method to add the control to the Controls collection. AddAt takes an additional parameter, an index into the array of controls. This is not an ideal solution, since you need to know which index to use. A better solution is to add an ASP.NET Placeholder control.
Switch back to AddControl.aspx by clicking on its tab in the editor or by double-clicking on AddControl.aspx in the Solution Explorer. Drag a PlaceHolder control from the Toolbox onto the page.
Switch back to the Page_Load event handler by double-clicking anywhere on the page. Change the following line
Dragging the PlaceHolder control onto the page inserted it between the <form runat=“server”> and </form> tags. Adding the TextBoxPlus control to the placeholder’s Controls collection ensures that the control will be rendered in the desired location (and one that won’t generate an exception!).
Save the changes, and then build the project. When you browse AddControl.aspx, the output should look similar to the following illustration.