Code modules that build a custom server control containing other controls can be built in two basic methods: compositional and noncompositional.
Compositional controls are so named because they include child controls that are added to the controls collection through code in the compiled control. The child controls are responsible for rendering themselves on the client and handling postback events.
The following code example illustrates a compositional control module:
// Example of a compositional control. using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace SampleControl { public class Sample : Control, INamingContainer // Add a property.to set and get textbox text. public int Value { get { return Int32.Parse(((TextBox)Controls[1]).Text); } set { ((TextBox)Controls[1]).Text = value.ToString(); } protected override void CreateChildControls() { // Add Literal Control - text box label. this.Controls.Add(new LiteralControl("<h3>Value: ")); // Add a Textbox TextBox box = new TextBox(); box.Text = "0"; this.Controls.Add(box); // Add Literal Control this.Controls.Add(new LiteralControl("</h3>")); // Add an "Add" Button // Clicking the button will increment the text box value. Button AddButton = new Button(); AddButton.Text = "Add"; AddButton.AddOnClick(new EventHandler(this.AddBtn_Click)); this.Controls.Add(AddButton); } // Add the event handler for the Add button. private void AddBtn_Click(Object sender, EventArgs e) { this.Value++; } } }
Noncompositional controls use the Render method of the Control class to write HTML and associated client-side event handling script to the client. While somewhat more difficult to write, noncompositional controls are preferred over compositional controls because of the overhead involved in instantiating child controls on compositional controls.
The following example shows how to override the Render method of the Control class to add some literal HTML text, a text control, and two button controls to a server page. This example creates a control with output equivalent to the previous example.
// Example of a non-compositional control. using System; using System.Web; using System.Web.UI; using System.Collections; using System.Web.UI.WebControls; namespace SampleControl { public class Sample : Control, IPostBackDataHandler, IPostBackEventHandler { private int _value = 0; public int Value { get { return _value; } set { _value = value; } } public bool LoadPostData(String postDataKey, NameValueCollection values) { _value = Int32.Parse(values[this.UniqueID]); return false; } public void RaisePostDataChangedEvent() { // Part of the IPostBackDataHandler contract. Invoked if we ever returned true from the // LoadPostData method (indicates that we want a change notification raised). Since we // always return false, this method is just a no-op. } public void RaisePostBackEvent(String eventArgument) { if (eventArgument == "Add") { this.Value++; } else { this.Value--; } } protected override void PreRender() { Page.RegisterPostBackScript(); } protected override void Render(HtmlTextWriter output) { output.Write("<h3>Value: <input name=" + this.UniqueID + " type=text value=" + this.Value + "> </h3>"); output.Write("<input type=button value=Add OnClick=\"jscript:"+ Page.GetPostBackEventReference(this, "Add")+ "\"> |"); output.Write("<input type=button value=Subtract OnClick=\"jscript:"+ Page.GetPostBackEventReference(this, "Subtract")+ "\">"); } } }
Note the call to the method GetPostBackEventReference, which automatically generates JavaScript event handling code that is sent to the client. The JavaScript code generates the HTTP POST in response to a button click event on the client.
The following ASP+ page, named SampleHtml.aspx, can use either the compositional or noncompositional control:
<%@ Register TagPrefix="SampleControl" Namespace="SampleControl" %> <html> <body> form method="POST" action="SampleHtml.aspx" runat=server> <SampleControl:Sample id="MyControl runat=server/> </form> </body> <html>