Advanced ASP.NET Server-side Controls, Part 2
George Shepherd
Download the code for this article:VisualProg0103.exe (33KB)
Browse the code for this article at Code Center:TempConvert

I

n January I looked at how to compose ASP.NET server-side controls from other standard controls. The example I offered was a Fahrenheit-to-Centigrade converter composed of two textboxes and two edit controls. However, rather than composing the server-side control from several standard controls as I did in the January issue, this month I'll render one on the fly. In this column I'll take a closer look at server-side controls, especially with respect to round-trips between the browser and the server, posting data, and developing the user interface using noncompositional techniques.

Server-side Controls

      Remember that one of the motivations behind server-side controls is that you can't count on one or two well-known client environments these daysâ€"the client may be any of a huge number of potential devices, ranging from small handheld devices to full-blown desktop computers. Using the Microsoft® .NET Framework, you could work around this problem by having the server generate sophisticated user interface code (which may depend upon the capabilities of the browser) as pure HTML and then squirt the HTML out to the browser. Because every browser (by definition) understands some form of HTML, generating the user interface for a browser this way is reasonable. Of course, you've always been able to write ASP code that generates the HTML in order to render controls on the screen. What's so different about server-side controls within ASP.NET? What ASP.NET brings to the table is a framework for standardizing and encapsulating user interface generation.
      In the simple control I designed in the January Visual Programmer column, the C# class representing the server-side control was composed of standard controls accessed through common language runtime (CLR) classes, and included the edit controls and buttons as member variables. The composite control also derived from the CLR Control class and implemented an interface named INamingContainer. The ASP.NET compositional control architecture is pretty straightforward. With composite controls, you don't have to override the Render method because the child controls provide their own rendering logic. Simply declaring the standard controls and adding them to the server-side control causes the server-side control to send the correct HTML to the browser. Then, by attaching event handlers to the standard controls, the control becomes interactive. The result is a collection of standard controls that collaborate as a single unit over an HTTP connection.

The Noncomposite Control

      The server-side control makes its appearance this month as a noncomposite, or rendered control. Rather than composing the server-side control from separate standard controls (like buttons and textboxes), rendered controls generate standard form elements on the browser (like buttons and edit boxes) by rendering the correct HTML in the browser. The difference here is that the noncompositional control renders the HTML from scratch through the control's Render method.
      The temperature converter control renders two textboxes and two pushbuttons, making them appear as a single component. Type a number into the Fahrenheit box, push the Fahrenheit To Centigrade button and the control converts Fahrenheit temperatures to Centigrade temperatures. On the other hand, type a number into the Centigrade box, push the Centigrade To Fahrenheit button and the control converts Centigrade temperatures to Fahrenheit temperatures. Figure 1 shows the control in action within Microsoft Internet Explorer 5.5.

Figure 1 Temperature Control in Action
Figure 1 Temperature Control in Action

      The temperature converter control is written in C# (though it could just as easily have been written in Visual Basic®). Figure 2 contains the code for the entire control. If you compare the code for the rendered control to the code for the composite control from January, you'll notice the rendered control requires more source code. That's because a rendered control doesn't delegate the responsibilities of rendering and data management. Here's a tour of the code, highlighting the more important aspects, including how to maintain properties in a control and how to respond to the child controls within the main control.

The Control Class

      The .NET CLR is the class library of the future. Rather than composing software using C++ or Visual Basic libraries, .NET user interface functionality is being pushed down into the runtime. Part of the runtime includes a collection of classes and interface implementations for creating controls.
      The System.Web.UI.Control class defines the properties, methods, and events that are shared by all server controls in the Web Forms page framework. The Control class has a constructor, maintains a collection of child controls to render, handles initialization and rendering, and maintains event hooks. I'll focus on the Render method for the rendered version of the temperature converter control.
      In addition to deriving from the CLR Control class, the temperature converter control implements two standard server-side control interfaces.

Server-side Control Interfaces

      The composite temperature control I covered in January derived from the CLR's Control class and implemented a single interfaceâ€"INamingContainer. The CLR's Control held a list of standard controls to render and automatically handled all data transfers between the browser and the server.
      Rendered controls require a bit more programming because it's up to the control developer to do the data transfer. For example, when the user pushes the button to convert a temperature, the Web page has to send the values back to the control so it can process the data. The data transfer is managed through two interfaces: IPostBackDataHandler and IPostBackEventHandler.
      A postback takes place when the Web page wants to send data back to the server. IPostBackDataHandler is implemented by controls wanting to receive postback data. After receiving a postback notification, the control may update its state and/or raise events on the server based on any changes to the data. The temperature converter control uses IPostBackDataHandler to get the new temperature value to convert.
      IPostBackDataHandler has two methods: LoadPostData and RaisePostDataChangedEvent. LoadPostData processes the postback data for the control. RaisePostDataChangedEvent signals the control to notify any listeners that the state of the control has changed.
      IPostBackEventHandler defines the contract that controls implement for handling low-level postback events. IPostBackEventHandler has a single method named RaisePostBackEvent, which enables a control to process the event fired by a form postback. I'll look at all of these methods a bit more closely later in this column.

Fahrenheit and Centigrade Values

      Most controls exist as a way to provide a user interface for managing a fine piece of data, which in turn is represented as a property within the control. The temperature converter control maintains two propertiesâ€"the actual Fahrenheit and Centigrade temperatures represented as doubles. C# promotes using accessor and mutator functions for managing a control's properties. Figure 2 contains the C# syntax for managing the Fahrenheit and Centigrade properties through accessor and mutator functions.
      The temperature control's Fahrenheit and Centigrade properties are declared as private data members and accessed through get and set functions. The C# syntax in Figure 2 uses accessor functions (the get functions) to be called when values are retrieved using the control's FahrenheitValue and CentigradeValue. Notice that this control simply gets the actual Fahrenheit value from the textbox representing the Fahrenheit value. Similarly, the mutator function (the set function) is called when assigning values to the control's FahrenheitValue and CentigradeValue; it stores the new values in the textbox representing the value. This makes managing the temperature values straightforward.

Rendering the Control

      The CLR's Control class provides a method, Render, for squirting text (usually some HTML) out to the browser. The Control class prepares an HtmlTextWriter object that manages the outgoing HTTP protocol to get text back to the browser. The main methods used during rendering are Write and WriteLine. Notice in Figure 2 that the temperature converter control overrides the Render method by pumping out HTML to generate two textboxes and two pushbuttons on the browser. The buttons are wired up to JavaScript handlers using a method named GetPostBackEventReference from the CLR Page class. Let's see how that works.

Generating a Postback Script

      While some HTML-generated controls (such as buttons) cause a postback to the originating page, others (such as textboxes) do not. If you need the form element to generate a postback to the server, you can do so through ASP.NET. ASP.NET manages custom postbacks through client-side JavaScript. To make this work for server-side controls, you enable it through the server-side control's Render and OnPreRender methods. Notice the Render method attaches button click handlers to the pushbuttons by going to the host page (represented by the Page object) and calling GetPostBackEventReference. GetPostBackEventReference sets up a reference to a client-side script function generated by the server-side control through a call to Page.RegisterPostBackScript. This client-side code generates a postback to the form.
      Because all of this user interface code is being generated from the server-side control, it becomes the server-side control's job to furnish the script. That is, the server-side control actually generates the event handlers mentioned in the previous paragraph. To program this in a server-side control, just override the control's OnPreRender method. Notice that the temperature converter code in Figure 2 overrides OnPreRender to call Page.RegisterPostBackScript. I'll look at the HTML rendered to the browser a bit later.

Loading the Posted Data

      Once the server-side control renders the form elements onto the browser, the user is free to type numbers into the textboxes and press the buttons. So what happens when the user presses a button to convert from one temperature scale to another? The browser has to submit the changes to the server.
      When the user hits the button (generating a postback to the server), the server-side control picks this event up in the LoadPostData method. The browser sends back the name of the control and a collection of values obtained from the form elements. To get the values associated with the textboxes, look them up in the Values collection using the key passed in as the first parameter. The values selected on the form come back in a comma-delimited list that you can parse to get the values. Figure 2 shows the temperature converter control pulling out the textbox data from the parameters passed into the LoadPostedData function.

Postback Events

      After getting the posted data from the browser, the temperature converter control needs to change its internal state to reflect the changes made on the browser. These events include calls to RaisePostBackDataChangedEvent and RaisePostBackEvent. Here's how the call sequence works.
      When a server-side control detects that the data posted back to the control is different from the data already inside the server-side control, LoadPostData returns true. The page framework keeps track of all the controls that have returned true from LoadPostData and calls RaisePostDataChangedEvent on those controls. At this point, the server-side control can do whatever it needs to do to synchronize the data appearing in the browser and the server-side control's internal state. The temperature converter control ignores this event.
      Finally, the page framework calls RaisePostBackEvent whenever the browser generates a postback to the server. When posting back, the browser sends down a string identifying the form element causing the postback. Notice that the temperature converter control checks to see if the postback was caused by the FahrenheitToCentigrade button or the CentigradeToFahrenheit button, and then does the appropriate conversion based on the data posted back during LoadPostData.

Using the Control on a Page

      Once the control is compiled into an assembly, you can use it within ASP.NET. Figure 3 shows a bit of ASP.NET code that declares an instance of the temperature converter. When the ASP.NET framework loads this page, ASP.NET writes out the body, center, and font tags to the browser. Then the form tag manages the input controls that will be emitted by the server-side control. For the complete picture, let's take a look at the HTML emitted by the server-side control.

HTML Rendered by the Control

      The basic job of an ASP.NET server-side control is to emit some markup language that the browser can deal with. Figure 4 shows the HTML emitted by the rendered temperature converter control. Most of this is standard HTML, rendering the textboxes and buttons on the browser.
      Notice the buttons have a JavaScript handler named __doPostBack attached to them. To understand how this works, look near the top of Figure 4 and notice the hidden fields and the JavaScript code. These elements were added by the ASP.NET page framework. The hidden form fields indicate which server control should be posted to, and may also specify an argument to be passed. The JavaScript method is used to set the hidden fields and cause the form to be submitted to the server.
      The beauty of the server-side control architecture is that you don't have to code those lines. Anytime you need a temperature conversion control, you can just drop in the temperature converter server-side control that I've shown here.


Send questions and comments for George to visprog@microsoft.com.

George Shepherd is an Associate Technology Director at Plural and delivers seminars with DevelopMentor. He is currently coauthoring Applied .NET to be published by Addison-Wesley later this year. Contact George at georges@develop.com.

From the March 2001 issue of MSDN Magazine