ptg 314 CHAPTER 7 Creating Custom Controls with User Controls AssociatedControlID=”txtCity” Runat=”server” /> </div> <div class=”addressField”> <asp:TextBox ID=”txtCity” Runat=”server” /> <asp:RequiredFieldValidator ID=”reqCity” Text=”(required)” ControlToValidate=”txtCity” Runat=”server” /> </div> <br class=”clear” /> <div class=”addressLabel”> <asp:Label ID=”lblState” Text=”State:” AssociatedControlID=”txtState” Runat=”server” /> </div> <div class=”addressField”> <asp:TextBox ID=”txtState” Runat=”server” /> <asp:RequiredFieldValidator ID=”reqState” Text=”(required)” ControlToValidate=”txtState” Runat=”server” /> </div> <br class=”clear” /> <div class=”addressLabel”> <asp:Label ID=”lblPostalCode” Text=”Postal Code:” AssociatedControlID=”txtPostalCode” Runat=”server” /> </div> <div class=”addressField”> <asp:TextBox ID=”txtPostalCode” From the Library of Wow! eBook ptg 315 Creating User Controls 7 Runat=”server” /> <asp:RequiredFieldValidator ID=”RequiredFieldValidator1” Text=”(required)” ControlToValidate=”txtPostalCode” Runat=”server” /> </div> <br class=”clear” /> </fieldset> The AddressForm control contains form controls for entering your street, city, state, and postal code. Each of these fields is validated by a RequiredFieldValidator control. Finally, the AddressForm includes a Label that can provide a title for the control. The AddressForm exposes all its form fields with properties. The control includes public Street, City, State, and PostalCode property, which you can read from the containing page. The page in Listing 7.11 illustrates how you can use the AddressForm control in a page. LISTING 7.11 Checkout.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”user” TagName=”AddressForm” Src=”~/AddressForm.ascx” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN” “http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”> <script runat=”server”> protected void btnSubmit_Click(object sender, EventArgs e) { // Show Billing Address Form Results ltlResults.Text = “<br />Billing Street: “ + AddressForm1.Street; ltlResults.Text += “<br />Billing City: “ + AddressForm1.City; ltlResults.Text += “<br />Billing State: “ + AddressForm1.State; ltlResults.Text += “<br />Billing Postal Code: “ + AddressForm1.PostalCode; ltlResults.Text += “<br /><br />”; // Show Shipping Address Form Results ltlResults.Text += “<br />Shipping Street: “ + AddressForm2.Street; ltlResults.Text += “<br />Shipping City: “ + AddressForm2.City; ltlResults.Text += “<br />Shipping State: “ + AddressForm2.State; ltlResults.Text += “<br />Shipping Postal Code: “ + AddressForm2.PostalCode; } </script> From the Library of Wow! eBook ptg 316 CHAPTER 7 Creating Custom Controls with User Controls <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <style type=”text/css”> html { background-color:silver; font:14px Georgia,Serif; } .content { background-color:white; width:600px; margin:auto; padding:20px; } .addressLabel { float:left; width:100px; padding:5px; text-align:right; } .addressField { float:left; padding:5px; } .clear { clear:both; } </style> <title>Checkout</title> </head> <body> <form id=”form1” runat=”server”> <div class=”content”> <user:AddressForm id=”AddressForm1” Title=”Billing Address” Runat=”server” /> <br /> From the Library of Wow! eBook ptg 317 AJAX and User Controls 7 <user:AddressForm id=”AddressForm2” Title=”Shipping Address” Runat=”server” /> <br /> <asp:Button ID=”btnSubmit” Text=”Submit Form” OnClick=”btnSubmit_Click” Runat=”server” /> <hr /> <asp:Literal id=”ltlResults” Runat=”server” /> </div> </form> </body> </html> The page in Listing 7.11 contains two instances of the AddressForm control: a Billing Address and Shipping Address. When you click the Button control, the address information is retrieved from the AddressForm controls and displayed in a Literal control. (In a real application, you would grab the data and store it in a database.) WEB STANDARDS NOTE The AddressForm User control does not use an HTML table to lay out its controls. You should strive to avoid using tables except when displaying tabular information. Instead, Cascading Style Sheet (CSS) rules are used to position the form elements. AJAX and User Controls Ajax (Asynchronous JavaScript and XML) enables you to update content in a page without posting the page back to the server. Behind the scenes, AJAX uses the XMLHttp ActiveX component (in the case of Microsoft Internet Explorer 6.0) or the XMLHttpRequest intrin- sic browser object (in the case of other browsers such as FireFox and Internet Explorer 8.0). We explore the topic of Ajax in depth in Part IX, “ASP.NET AJAX.” In this section, I want to provide you with a quick sample of using Ajax with a User control. The User control in From the Library of Wow! eBook ptg 318 CHAPTER 7 Creating Custom Controls with User Controls Listing 7.12 randomly displays one of three quotations. The quotation is updated auto- matically every 5 seconds (see Figure 7.4). FIGURE 7.4 Using AJAX to display a random quotation. LISTING 7.12 RandomQuotation.ascx <%@ Control Language=”C#” ClassName=”RandomQuotation” %> <%@ Import Namespace=”System.Collections.Generic” %> <script runat=”server”> void Page_Load() { List<string> quotes = new List<string>(); quotes.Add(“All paid jobs absorb and degrade the mind Aristotle”); quotes.Add(“No evil can happen to a good man, either in life or after death Plato”); quotes.Add(“The only good is knowledge and the only evil is ignorance Plato”); Random rnd = new Random(); lblQuote.Text = quotes[rnd.Next(quotes.Count)]; } </script> From the Library of Wow! eBook ptg 319 AJAX and User Controls 7 <asp:ScriptManager ID=”sm1” runat=”server” /> <asp:Timer ID=”Timer1” Interval=”5000” runat=”server” /> <asp:UpdatePanel ID=”up1” runat=”server”> <Triggers> <asp:AsyncPostBackTrigger ControlID=”Timer1” /> </Triggers> <ContentTemplate> <div class=”quote”> <asp:Label id=”lblQuote” Runat=”server” /> </div> </ContentTemplate> </asp:UpdatePanel> A random quotation is assigned to the Label control in the Page_Load() method in Listing 7.12. The Label control is contained in an UpdatePanel control. The UpdatePanel marks an area of the page that is refreshed without a postback (an Ajax zone). The UpdatePanel control is associated with a Timer control. The Timer control causes the UpdatePanel to refresh its content every 5 seconds (an interval of 5,000 milliseconds). The page in Listing 7.13 illustrates how you can use the RandomQuotation User control. It contains the User control and also displays the current time. LISTING 7.13 ShowRandomQuotation.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”user” TagName=”RandomQuotation” Src=”~/RandomQuotation.ascx” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN” “http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <style type=”text/css”> .quote { width:200px; padding:20px; border:Dotted 2px orange; background-color:#eeeeee; font:16px Georgia,Serif; } </style> <title>Show Random Quotation</title> From the Library of Wow! eBook ptg 320 CHAPTER 7 Creating Custom Controls with User Controls </head> <body> <form id=”form1” runat=”server”> <div> <%= DateTime.Now %> <br /> <user:RandomQuotation id=”RandomQuotation1” Runat=”server” /> </div> </form> </body> </html> The random quotation is updated, but the time on the page does not change. Only the area of the page that contains the random quotation is updated. Dynamically Loading User Controls You can dynamically load a User control at runtime and display it in a page. Imagine, for example, that you want to display different featured products randomly on the home page of your website. However, you want to display each featured product with a completely different layout. In that case, you can create a separate User control for each product and load one of the User controls randomly at runtime. You load a User control with the Page.LoadControl() method, which returns an instance of the Control class that you can add to a page. Typically, you add the User control to a PlaceHolder control that you have declared on the page. NOTE The PlaceHolder control was designed to do absolutely nothing. It simply acts as a placeholder on the page where you can add other controls. For example, the page in Listing 7.14 randomly loads one of the controls from the FeaturedProducts folder and adds the control to the page. From the Library of Wow! eBook ptg 321 Dynamically Loading User Controls 7 LISTING 7.14 ShowFeaturedProduct.aspx <%@ Page Language=”C#” %> <%@ Import Namespace=”System.IO” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN” “http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”> <script runat=”server”> const string randomFolder = “~/FeaturedProducts”; protected void Page_Load(object sender, EventArgs e) { string featuredProductPath = GetRandomProductPath(); Control featuredProduct = Page.LoadControl(featuredProductPath); PlaceHolder1.Controls.Add(featuredProduct); } private string GetRandomProductPath() { Random rnd = new Random(); string[] files = Directory.GetFiles(MapPath(randomFolder), “*.ascx”); string featuredProductPath = ➥ Path.GetFileName(files[rnd.Next(files.Length)]); return Path.Combine(randomFolder, featuredProductPath); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show Featured Products</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:PlaceHolder id=”PlaceHolder1” Runat=”server” /> </div> </form> </body> </html> From the Library of Wow! eBook ptg 322 CHAPTER 7 Creating Custom Controls with User Controls Using the Reference Directive When you load a User control with the Page.LoadControl() method, the User control is returned as an instance of the System.Web.UI.Control class. This means that if the User control includes any custom properties, the properties aren’t available when you dynami- cally load the User control. If you dynamically load a User control, you need to cast the control to the correct type before you can access any of the control’s custom properties. To get a reference to a User control’s type, you must use the <%@ Reference %> directive. For example, imagine that you need to create a form that displays different questions depending on the answers that a user provides for previous questions. In that case, you can dynamically load different User controls that contain the different sets of questions. For example, the page in Listing 7.15 contains a survey form. The first question asks you whether you currently use ASP Classic or ASP.NET. Depending on your answer, the remain- der of the form displays different questions (see Figure 7.5). FIGURE 7.5 Displaying a survey form with dynamically loaded questions. From the Library of Wow! eBook ptg 323 Dynamically Loading User Controls 7 LISTING 7.15 WebSurvey.aspx <%@ Page Language=”C#” %> <%@ Reference Control=”~/ASPSurvey.ascx” %> <%@ Reference Control=”~/ASPNetSurvey.ascx” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN” “http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”> <script runat=”server”> private Control _survey = null; void Page_Load() { switch (ddlLanguage.SelectedIndex) { case 1: _survey = Page.LoadControl(“ASPSurvey.ascx”); break; case 2: _survey = Page.LoadControl(“ASPNetSurvey.ascx”); break; } if (_survey != null) PlaceHolder1.Controls.Add(_survey); } protected void btnSubmit_Click(object sender, EventArgs e) { switch (ddlLanguage.SelectedIndex) { case 1: ASPSurvey aspResults = (ASPSurvey)_survey; ltlResults.Text = “<h1>ASP Survey</h1>”; ltlResults.Text += “<br />Know slow? “ + ➥ aspResults.KnowSlow.ToString(); ltlResults.Text += “<br />Know outdated? “ + ➥ aspResults.KnowOutdated.ToString(); break; case 2: ASPNetSurvey aspNetResults = (ASPNetSurvey)_survey; ltlResults.Text = “<h1>ASP.NET Survey</h1>”; ltlResults.Text += “<br />Know fast? “ + ➥ aspNetResults.KnowFast.ToString(); ltlResults.Text += “<br />Know newest? “ + ➥ aspNetResults.KnowNewest.ToString(); From the Library of Wow! eBook . />Know outdated? “ + ➥ aspResults.KnowOutdated.ToString(); break; case 2: ASPNetSurvey aspNetResults = (ASPNetSurvey)_survey; ltlResults.Text = “<h1> ;ASP. NET Survey</h1>”; ltlResults.Text. or the XMLHttpRequest intrin- sic browser object (in the case of other browsers such as FireFox and Internet Explorer 8.0). We explore the topic of Ajax in depth in Part IX, ASP. NET AJAX.” In. (ddlLanguage.SelectedIndex) { case 1: ASPSurvey aspResults = (ASPSurvey)_survey; ltlResults.Text = “<h1> ;ASP Survey</h1>”; ltlResults.Text += “<br />Know slow? “ + ➥ aspResults.KnowSlow.ToString();