Next, a couple of local member variables are declared. The location of the declaration is important, since these members need to be accessible to any procedure in the control. A property procedure is then added for the ShowDividers property, which will determine whether the control renders a horizontal line between each node of the control.
In the NavBar_Load method, which handles the Load event for the control (fired automatically by ASP.NET), the LoadData method is called to load the NavBar data from the XML file associated with the control.
Skipping over the Render method temporarily, the LoadData method creates a new instance of the ADO.NET
DataSet class and calls its ReadXml method to read the data from the XML file. If no file exists, the LoadData method calls another method (CreateBlankFile) to create a blank XML file with the correct format for use by the developer consuming the control. This technique not only deals gracefully with an error condition; it provides an easier starting point for the developer using the control. Note that the CreateBlankFile method is declared as public, which means it can be called deliberately to create a blank file, if desired.
Last, but certainly not least, the overridden Render method, which is called automatically at runtime when the control is created, iterates through the first (and only) table in the dataset and uses an instance of the
StringBuilder class to build the HTML output to render. Once the desired output has been built, the method uses the HtmlTextWriter passed to it by ASP.NET to write the output to the client browser. Note that prior to looping through the rows in the dataset, the render method calls the RenderBeginTag and RenderContents methods of the base Panel control. This renders the opening
<div> tag that is the client-side representation of the Panel control, plus anything contained within the opening and closing tags of the NavBar control. Once all the rows have been iterated and their output sent to the browser, the RenderEndTag method is called to send the closing
</div> tag to the browser.
You can compile the code in Example 6-4 by using the following single-line command (which can alternatively be placed in a batch file):
vbc /t:library /out:bin\NavBar.dll /r:System.dll,System.Data.dll,
The preceding command requires that you create a bin subdirectory under the directory from which the command is launched and that you register the path to the Visual Basic compiler in your
PATH environment variable. If you have not registered this path, you will need to provide the full path to the Visual Basic .NET compiler (by default, this path is %windir%\Microsoft.NET\Framework\%version%).
Example 6-5: NavBar.xml
<navBar> <!-- node field describes a single node of the control --> <node> <!-- Required Fields --> <!-- url field should contain the absolute or relative URL to link to --> <url>NavBarClient.aspx</url> <!-- text field should contain the descriptive text for this node --> <text>NavBar Client</text> <!-- End Required Fields --> <!-- Optional Fields --> <!-- imageUrl field should contain the absolute or relative URL for an image to be displayed in front of the link --> <imageUrl>node.jpg</imageUrl> <!-- moimageUrl field should contain the absolute or relative URL for an image to be displayed in front of the link on mouseover --> <moImageUrl>node_rev.jpg</moImageUrl> <!-- targetFrame field should contain one of the following: _blank, _parent, _self, _top --> <targetFrame>_self</targetFrame> <!-- End Optional Fields --> </node> <node> <url>UCClient.aspx</url> <text>User Control Client</text> <imageUrl>alt_node.jpg</imageUrl> <moImageUrl>alt_node_rev.jpg</moImageUrl> <targetFrame>_self</targetFrame> </node> <node> <url>BlogClient.aspx</url> <text>Blog Client</text> <imageUrl>node.jpg</imageUrl> <moImageUrl>node_rev.jpg</moImageUrl> <targetFrame> </targetFrame> </node> <node> <url>BlogAdd.aspx</url> <text>Add New Blog</text> <imageUrl>alt_node.jpg</imageUrl> <moImageUrl>alt_node_rev.jpg</moImageUrl> <targetFrame> </targetFrame> </node> </navBar>
Example 6-6: NavBarClient.aspx
<%@ Page Language="vb" %> <%@ Register TagPrefix="aspnetian" Namespace="aspnetian" Assembly="NavBar" %> <html> <head> <script runat="server"> Sub Page_Load( ) 'NB1.CreateBlankFile( ) End Sub </script> </head> <body> <table border="1" width="100%" cellpadding="20" cellspacing="0"> <tr> <td align="center" width="150"> <img src="aspnetian.jpg"/> </td> <td align="center"> <h1>NavBar Control Client Page<h1> </td> </tr> <tr> <td width="150"> <form runat="server"> <aspnetian:NavBar id="NB1" showdividers="False" runat="server"> <strong>Navigation Bar</strong> <br/> </aspnetian:NavBar> </form> </td> <td> This is where page content might be placed <br/><br/><br/><br/><br/><br/><br/><br/><br/> </td> </tr> </table> </body> </html>
As mentioned earlier in the chapter, compositional controls render their output by combining appropriate controls within the CreateChildControls method, which is overridden in the custom control.
Example 6-7 shows the C# code for a compositional control that provides simple functionality for a blog (which is short for web log). The control has two modes: Add and Display. The mode is determined by the internal member _mode, which can be accessed by the public Mode property.
Like the NavBar control created in the previous example, the class definition for the Blog control specifies that the class derives from the Panel control (using C#'s
: syntax), and also implements the INamingContainer interface. The INamingContainer interface contains no members, so there's nothing to actually implement. It's simply used to tell the ASP.NET runtime to provide a separate naming scope for controls contained within the custom control. This helps avoid the possibility of naming conflicts at runtime.
Also like the NavBar control, the Blog control uses an XML file to store the individual Blog entries. The example uses the same method of retrieving the data, namely creating a dataset and calling its ReadXml method, passing in the name of the XML file.
In addition to declaring the _mode member variable and the BlogDS dataset, the example declares two Textbox controls (which will be used when adding a new blog entry) and two more string member variables (_addRedirect and _email).
The code in Example 6-7 then creates public property accessors for all three string variables. The Mode property determines whether the control displays existing blogs or displays fields for creating a new blog. The AddRedirect property takes the URL for a page to redirect to when a new blog is added. The Email property takes an email address to link to in each new blog field.
Next, the program overrides the OnInit method of the derived control to handle the Init event when it is called by the runtime. In this event handler, you call the LoadData method, which, like the same method in the NavBar control, loads the data from the XML file or, if no file exists, creates a blank file. It then calls the OnInit method of the base class to ensure that necessary initialization work is taken care of.
Next is the overridden CreateChildControls method. Like the Render method, this method is called automatically by the ASP.NET runtime when the page is instantiated on the server. Unlike the Render method, you don't want to call the CreateChildControls method of the base class, or you'll create a loop in which this method calls itself recursively (and the ASP.NET process will hang). In the CreateChildControls method, you check the value of the _mode member variable and call either the DisplayBlogs method or the NewBlog method, depending on the value of _mode. Note that this value is set by default to
display, so if the property is not set, the control will be in display mode. Also note that the example uses the ToLower method of the
String class to ensure that either uppercase or lowercase attribute values work properly.
The DisplayBlogs method iterates through the data returned in the dataset and instantiates controls to display this data. We use an
if statement to determine whether more than one entry in a row has the same date. If so, we display only a single date header for the group of entries with the same date. We add an HtmlAnchor control to each entry to facilitate the readers' ability to bookmark the URL for a given entry. Then we write out the entry itself and add a contact email address and a link to the specific entry at the end of each entry.