Dynamic Loading (and Cookies, Too)

Dynamic Loading (and Cookies, Too!)

It s easy enough to create user controls declaratively by including tags in your ASPX files. But user controls can also be created programmatically, a procedure referred to as dynamic loading. The Page class s LoadControl method loads a user control at run time. It takes an ASCX file name as input and returns a generic Control reference representing the control that it loaded. The following example does away with the need for a <user:XmlNavBar> tag by loading XmlNavBar dynamically:

Control navbar = LoadControl ("XmlNavBar.ascx"); Controls.Add (navbar);

The first statement loads the control. The second adds it to the page by calling Add on the page s Controls collection. In practice, adding a dynamically loaded control to the page is rarely this simple. Typically, you call Add on the Controls collection of a table cell or a <span> element to control where on the page the control appears. If the container hosts other controls as well, you can use AddAt instead of Add to specify where in the container s Controls collection the new control should be added.

LoadControl makes dynamic loading a breeze. The only catch is that the Control reference that it returns doesn t enable you to access any of the methods, properties, or events defined in the ASCX file. The following code, for example, won t compile because XmlSrc isn t a member of Control:

Control navbar = LoadControl ("XmlNavBar.ascx"); // Attempt to point the control to an XML source file navbar.XmlSrc = "Links.xml"; // Won't compile!

The solution is to cast the Control reference to an XmlNavBar_ascx reference:

XmlNavBar_ascx navbar = (XmlNavBar_ascx) LoadControl ("XmlNavBar.ascx"); // Attempt to point the control to an XML source file navbar.XmlSrc = "Links.xml"; // It works!

Where did XmlNavBar_ascx come from? That s the name of the class that ASP.NET generated to represent the XmlNavBar control. It simply strips the file name extension from the ASCX file s file name and appends _ascx. If you d prefer to name the class yourself, you can do so by including an @ Control directive in the ASCX file:

<%@ Control ClassName="XmlNavBarControl" %>

The code to load and initialize the control would then look like this:

XmlNavBarControl navbar = (XmlNavBarControl) LoadControl ("XmlNavBar.ascx"); navbar.XmlSrc = "Links.xml";

Regardless of whether you specify a class name or settle for the default, you must register the control with an @ Register directive if you cast LoadControl s return value. Otherwise, the compiler will complain that the type you cast to is an undefined type.

The question you re probably asking yourself is, Why would I want to load a user control dynamically? The answer is that it s a great way to customize a Web form on the basis of user preferences. Suppose you offer your users the option of displaying a stock ticker on your company s home page. You could implement the stock ticker as a user control and load it at run time only for those users who indicate that they want the stock ticker to appear. Users who don t want a stock ticker won t be bothered by it, and those who want one will get their wish.

Cookies

One problem with writing a Web page that supports user preferences is figuring out where to store those preferences. HTTP is a stateless protocol. As a user browses your site, all the Web server sees is a succession of HTTP requests coming from an arbitrary IP address. Chances are those requests are mixed with requests coming from other IP addresses, too. To store preferences, you either have to store them on the server and somehow correlate each incoming HTTP request to a set of stored preferences, or come up with a way to encode the user preferences in the individual requests. Cookies are a convenient mechanism for doing the latter.

A cookie is nothing more than a chunk of data that s transmitted from a browser to a Web server in each and every request. Cookies are transmitted in HTTP headers specifically, in Cookie headers. Here s an HTTP request containing a cookie:

GET /mypage.html HTTP/1.1 Accept: */*   .   .   . User-Agent: Mozilla/4.0.(compatible; MSIE.6.0; Windows NT 5.1) Host: www.wintellect.com Cookie: FavoriteColor=Blue

Cookies have names and values. In this example, the cookie s name is FavoriteColor, and its value is Blue. A browser can transmit several cookies at once by encoding them in a semicolon-separated list, as in

Cookie: FavoriteColor=Blue; FavoriteNumber=3

Lots of Web sites use cookies to identify returning users. Sometimes cookies contain user preferences; other times they carry authentication information to prevent you from having to log in again and again to sites that require logins. ASP.NET uses cookies to correlate returning visitors to sessions stored on the Web server something you ll learn all about in the next chapter. ASP.NET also uses cookies to identify users who ve been validated using forms authentication. (I ll cover forms authentication in more detail in Chapter 10.) Here s a cookie that could be used to signify that the requestor wants to see the latest prices of Amazon, Intel, and Microsoft stock on a personalized home page:

Cookie: Stocks=AMZN&INTC&MSFT

When a request containing a cookie arrives, it s up to the server to parse and interpret the cookie. Cookies are highly application-specific. Their purpose and contents vary widely among applications that use them.

Most browsers permit cookies to be disabled. Some sites warn you that they won t work properly if cookies aren t enabled. That s usually because they use cookies to round-trip user preferences, login information, or something similar. Cookies have gotten a bad rap in the press, but by and large they re harmless and in fact are quite useful to have around because we re stateful beings who use a stateless protocol as our primary means of transmitting digital information around the world.

How are cookies created? How does a browser know when to send a cookie and what to send? A Web server creates a cookie by returning it in the Set-Cookie header of an HTTP response. The following response returns a cookie to the requestor:

HTTP/1.1 200 OK Server: Microsoft-IIS/5.0 Date: Wed, 24 Oct 2001 14:12:37 GMT Content-Type: text/html Content-Length: 46   .   .   . Set-Cookie: FavoriteColor=Blue; path=/

The Path component of the Set-Cookie header tells the requestor which subset of URLs at this domain the cookie applies to. The path=/ attribute says that the cookie should be transmitted in every request to this domain. By default, this domain is the domain that the original request was directed to. However, cookies can optionally include Domain attributes that identify other domains. For example, if the Web server returns this Set-Cookie header:

Set-Cookie: FavoriteColor=Blue; domain=www.wintellect.com/; path=/

the FavoriteColor cookie will be transmitted in subsequent requests sent to www.wintellect.com, regardless of what domain the cookie was created in. Browsers don t have to honor Set-Cookie headers, but most do if cookies aren t disabled. A browser that accepts a cookie caches it so that it can transmit it back to the server in future requests.

How the browser caches a cookie depends on the cookie type. Session cookies are valid only until the browser closes and are typically cached in memory. Persistent cookies have deterministic lifetimes that are independent of the browser s lifetime. They re stored on the client s hard disk. (If you re an Internet Explorer user, look in Documents and Settings\UserName\Cookies to peek at the cookies stored on your hard disk.) What differentiates a session cookie from a persistent cookie, and how does the browser know when a persistent cookie expires? The Expires attribute. The following response header creates a persistent cookie that expires at noon on December 31, 2009:

Set-Cookie: FavoriteColor=Blue; expires=Thu, 31-Dec-2009 12:00:00 GMT; path=/

A cookie that lacks an Expires attribute is a session cookie; a cookie with an Expires attribute is a persistent cookie. Cookies can also include Secure attributes that prevent browsers from transmitting them over unencrypted (non-HTTPS) channels. The Secure attribute is handy if you use cookies to transmit sensitive information that mustn t fall into the wrong hands.

Cookies and ASP.NET

The .NET Framework class library simplifies cookie usage by providing a wrapper class named HttpCookie (a member of the System.Web namespace). One simple statement creates a cookie and assigns it a name and a value:

HttpCookie cookie = new HttpCookie ("FavoriteColor", "Blue");

Public HttpCookie properties named Domain, Path, Expires, and Secure wrap the cookie attributes of the same names. The following statement sets a cookie to expire one week from today:

cookie.Expires = DateTime.Now + new TimeSpan (7, 0, 0, 0);

These statements assign the cookie an absolute (rather than relative) expiration date and tag it with a Secure attribute, too:

cookie.Expires = new DateTime (2009, 12, 31); // December 31, 2009 cookie.Secure = true;

If you want to change a cookie s name or value after creating it, you can do so by using the cookie s Name and Value properties.

HttpCookie makes cookie creation simple. But how do you return a cookie that you create with HttpCookie to the browser in a Set-Cookie header? Easy: you add the cookie to the Response object s Cookies collection. The following statements create a session cookie named FavoriteColor (note the lack of statements assigning the cookie an expiration date, which makes the cookie a session cookie) and add it to the HTTP response:

HttpCookie cookie = new HttpCookie ("FavoriteColor", "Blue"); Response.Cookies.Add (cookie);

It s that simple. Adding the cookie to the Response object s Cookies collection ensures that the cookie will be returned in the HTTP response. You can add as many cookies as you like. They ll all be returned in the response.

Of course, cookies are of no use at all unless you can read them from incoming requests. To that end, the ASP.NET Request object contains a Cookies collection of its own that exposes the cookies (if any) accompanying an HTTP request. The following code reads a single cookie (by name) from the request, verifies that the cookie is indeed present in the request, and extracts the cookie s value:

HttpCookie cookie = Request.Cookies["FavoriteColor"]; if (cookie != null) {     string FavoriteColor = cookie.Value;       .       .       . }

The Request object s Cookies property is of type HttpCookieCollection. If you d like, you can use HttpCookieCollection properties such as Keys, AllKeys, and Count to enumerate the cookies accompanying an HTTP request.

Cookies can be deleted by the user or by the Web server. A user destroys a session cookie by simply closing his or her browser. Persistent cookies are destroyed by deleting the files they re stored in. That s typically done using a browser command (such as Internet Explorer s Delete Cookies command), but the operation can be performed manually or with the help of third-party utilities, too. Web servers delete cookies by doing the following:

  • Returning Set-Cookie headers containing the names of the cookies to be deleted, accompanied by null values

  • Including in those Set-Cookie headers expiration dates identifying dates in the past

This Set-Cookie header commands the browser to delete the cookie named FavoriteColor, and works regardless of whether FavoriteColor is a session cookie or a persistent cookie:

Set-Cookie: FavoriteColor=; expires=Wed, 30-Sep-1959 07:15:00 GMT

In an ASP.NET application, you delete the FavoriteColor cookie this way:

HttpCookie cookie = new HttpCookie ("FavoriteColor"); cookie.Expires = new DateTime (1959, 9, 30); Response.Cookies.Add (cookie);

Passing a single value to HttpCookie s constructor assigns the cookie a name but no value, producing a Set-Cookie header very much like the one just shown.

Multivalue Cookies

It s not uncommon for cookie values to include multiple name/value pairs. For example, the following Cookie header transmits a cookie containing a user name and password:

Cookie: AuthInfo=UserName=Jeffpro&Password=imbatman

This is perfectly legal because the cookie technically still has just one value: UserName=Jeffpro&Password=imbatman. The application running on the server can read that value and parse it to extract its subitems.

To make dealing with multivalue cookies easier (and to prevent you from having to manually parse out the subitems), HttpCookie exposes properties named HasKeys and Values. HasKeys is a Boolean; it reveals whether the cookie value contains subitems. Values is a collection that exposes the subitems. The following statements create and return a cookie that contains two subitems:

HttpCookie cookie = new HttpCookie ("AuthInfo"); cookie.Values["UserName"] = "Jeffpro"; cookie.Values["Password"] = "imbatman"; Response.Cookies.Add (cookie);

And this code retrieves the user name and password encapsulated in the cookie from the HTTP request:

HttpCookie cookie = Request.Cookies["AuthInfo"]; if (cookie != null) {     string UserName = cookie.Values["UserName"];     string Password = cookie.Values["Password"]; }

You can also enumerate the subitems contained in the Values property by using members of the NameValueCollection class. (Values is of type NameValueCollection.) Enumeration is useful when you don t know the names of the subitems up front.

The MyQuotes Page

The application in Figures 7-15 and 7-16 demonstrates how dynamically loaded user controls and cookies can be combined to create personalized Web pages. When initially displayed, MyQuotes.aspx contains nothing but a check box labeled Show quotes. Checking the box dynamically loads the user control defined in MyQuotes.ascx and adds it to the page. MyQuotes.ascx goes out on the Web, fetches the most recent quotes for Amazon, Intel, and Microsoft stock, and displays them in a DataGrid. Refreshing the page refreshes the stock prices as well.

Checking the Show Quotes box does more than just load MyQuotes.ascx; it also creates a cookie named MyQuotes and returns it to the requestor. The cookie is a persistent one that s good for one year. Each time the page loads, the Page_Load handler checks for the MyQuotes cookie and loads the user control if the cookie is found. Consequently, you can check the box, close and reopen your browser, and go back to MyQuotes.aspx, and the user control comes back automatically. Unchecking the box deletes the cookie and prevents the control from being loaded again.

Figure 7-15

The MyQuotes page.

The stock quotes displayed by the user control are real, although they re delayed by 20 minutes. How does MyQuotes fetch real-time (or near-real-time) stock prices? It uses a Web service. Specifically, it uses the Delayed Stock Quote Web service written and supported by XMethods (www.xmethods.com). I haven t formally introduced Web services just yet, but you ll learn all about them in Chapter 11. I decided to use one here to make the sample more tantalizing. The downside to using a Web service is that there s no guarantee that it will be there tomorrow. MyQuotes worked fine when I wrote it, but if by some chance the Delayed Stock Quote service is no longer available when you read this, or if its URL or programmatic interface has changed, MyQuotes will be a dull sample indeed.

In order to run MyQuotes, you must copy the supplied DLL (MyQuotes.dll) to the bin directory of the application root. If MyQuotes.aspx and MyQuotes.ascx are in wwwroot, the DLL must be in wwwroot\bin. MyQuotes.dll contains a Web service proxy class named netxmethodsservices stockquoteStockQuoteService that enables the user control to talk to the Delayed Stock Quote service using SOAP (Simple Object Access Protocol) messages transmitted over HTTP. Thanks to the proxy class, retrieving stock prices is as simple as this (with exception-handling code omitted for clarity):

netxmethodsservicesstockquoteStockQuoteService qs =     new netxmethodsservicesstockquoteStockQuoteService (); decimal amzn; decimal intc; decimal msft; amzn = (decimal) qs.getQuote ("AMZN"); intc = (decimal) qs.getQuote ("INTC"); msft = (decimal) qs.getQuote ("MSFT");

MyQuotes.dll was generated from the source code file MyQuotes.cs, which in turn was generated by Wsdl.exe. Don t worry if this makes no sense right now; it ll become crystal clear in Chapter 11. A lot of people feel that Web services are the future of the Internet. If this sample doesn t make you hungry to know more, I don t know what will.

MyQuotes.aspx

<%@ Register TagPrefix="user" TagName="MyQuotes" src="/books/4/347/1/html/2/MyQuotes.ascx" %> <html>   <body>     <h1>MyQuotes</h1>     <hr>     <form runat="server">       <asp:CheckBox  Text="Show quotes" RunAt="server"         AutoPostBack="true" OnCheckedChanged="OnCheckBoxClicked" />       <span  runat="server" />     </form>   </body> </html> <script language="C#" runat="server">   const string CookieName = "MyQuotes";   const string CookieVal = "ShowQuotes";   void Page_Load (Object sender, EventArgs e)   {       if (!IsPostBack) {           // If a "MyQuotes" cookie is present, load the           // user control and check the "Show quotes" box           HttpCookie cookie = Request.Cookies["MyQuotes"];           if (cookie != null) {               MyQuotes_ascx ctrl =                   (MyQuotes_ascx) LoadControl ("MyQuotes.ascx");               ctrl.Width = Unit.Percentage (100);               PlaceHolder.Controls.Add (ctrl);               ShowQuotes.Checked = true;           }       }   }   void OnCheckBoxClicked (Object sender, EventArgs e)   {       if (ShowQuotes.Checked) {           // Load the user control           MyQuotes_ascx ctrl =               (MyQuotes_ascx) LoadControl ("MyQuotes.ascx");           ctrl.Width = Unit.Percentage (100);           PlaceHolder.Controls.Add (ctrl);           // Return a "MyQuotes" cookie that's good for one year           HttpCookie cookie = new HttpCookie (CookieName, CookieVal);           cookie.Expires = DateTime.Now + new TimeSpan (365, 0, 0, 0);           Response.Cookies.Add (cookie);       }

      else {           // Delete the "MyQuotes" cookie           HttpCookie cookie = new HttpCookie (CookieName);           cookie.Expires = new DateTime (1959, 9, 30);           Response.Cookies.Add (cookie);       }   } </script>

Figure 7-16

MyQuotes source code.

MyQuotes.ascx

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Drawing" %> <asp:DataGrid  RunAt="server"   BorderWidth="1" BorderColor="lightgray" CellPadding="2"   Font-Name="Verdana" Font-Size="8pt" GridLines="vertical">   <HeaderStyle BackColor="maroon" ForeColor="white"     HorizontalAlign="center" />   <ItemStyle BackColor="white" ForeColor="black" />   <AlternatingItemStyle BackColor="beige" ForeColor="black" /> </asp:DataGrid> <span style="font-family: verdana; font-size: 8pt">   Quotes are delayed by 20 minutes </span> <script language="C#" runat="server">   Unit MyWidth;   public Unit Width   {       get { return MyWidth; }       set { MyWidth = value; }   }   void Page_Load (Object sender, EventArgs e)   {       if (MyWidth != Unit.Empty)           MyGrid.Width = MyWidth;       // Get quotes for AMZN, INTC, and MSFT       netxmethodsservicesstockquoteStockQuoteService qs =           new netxmethodsservicesstockquoteStockQuoteService ();       decimal amzn;       decimal intc;       decimal msft;       try {           amzn = (decimal) qs.getQuote ("AMZN");       }       catch {           amzn = -1.0m;       }       try {           intc = (decimal) qs.getQuote ("INTC");       }       catch {           intc = -1.0m;       }       try {           msft = (decimal) qs.getQuote ("MSFT");       }       catch {           msft = -1.0m;       }       // Add the quotes to a DataSet       DataSet ds = new DataSet ();       DataTable dt = new DataTable ("Quotes");       ds.Tables.Add (dt);       DataColumn col1 = new DataColumn ("Symbol", typeof (string));       DataColumn col2 = new DataColumn ("Price", typeof (string));       dt.Columns.Add (col1);       dt.Columns.Add (col2);       DataRow row = dt.NewRow ();       row["Symbol"] = "AMZN";       row["Price"] = (amzn == -1.0m) ? "Unavailable" :           String.Format ("{0:c}", amzn);       dt.Rows.Add (row);       row = dt.NewRow ();       row["Symbol"] = "INTC";       row["Price"] = (intc == -1.0m) ? "Unavailable" :           String.Format ("{0:c}", intc);       dt.Rows.Add (row);       row = dt.NewRow ();       row["Symbol"] = "MSFT";       row["Price"] = (msft == -1.0m) ? "Unavailable" :           String.Format ("{0:c}", msft);       dt.Rows.Add (row);       // Bind the DataGrid to the DataSet       MyGrid.DataSource = ds;       MyGrid.DataBind ();   } </script>



Programming Microsoft  .NET
Applied MicrosoftNET Framework Programming in Microsoft Visual BasicNET
ISBN: B000MUD834
EAN: N/A
Year: 2002
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net