Creating the Presentation with User Controls

Creating the Presentation with User Controls

Most likely the best piece of advice you can get is to create your ASP.NET user interfaces with user controls. Referred to as control compositing , you can design a user control precisely as you would a Web page. The only difference is that user controls can be reorganized and reused on one or many Web pages. An excellent example of this technique is the very dynamic IBuySpy portal application available for download from Microsoft at http://www.asp.net. The portal refers to its user controls as portal modules . Each module shares a common base class, and the portal can be updated after the portal application has been deployed with an administrative login.

TIP

You can download and use any or all of the IBuySpy portal code without cost, as many companies are doing.

I have used a similar technique with great success on a couple of projects and am employing this device as a reimplementation of http://www.softconcepts.com in ASP.NET after eight years as an HTML-based Web site. Some of the examples I will show you next demonstrate the compositing technique recommended by Microsoft and promoted as a good object-oriented approach in general.

Creating a Basic User Control Layout

Earlier I demonstrated how you can use an HTML table to manage the layout of a Web page. This technique is not entirely new; it's more in the category of "an oldie but a goody." I first used the technique when I realized in the mid-1990s that not every Web browser supported frames, but I wanted the compartmentalization of frames . More recently we used HTML tables on a project because some developers weren't happy with the control they had over layout, finding screen creation a bit tedious . Because user controls are essentially designable like Web pages we can employ the same HTML table technique to manage layout of user controls.

NOTE

Any recommended approach represents one of many ways to accomplish a given task. Here I'm promoting compositing through user controls as a best practice. As with any rules, experience dictates that sometimes you have to deviate from known best practices. That's how new best practices evolve . If you have found useful another way of implementing Web pages, feel free to send me an e-mail and a sample at pkimmel@softconcepts.com.

The header control in Figure 15.7 is comprised of several user controls. I like to think of layering controls and compositing like hammered steel . Individually each control is easy to build; together they create a strong and resilient end result. The links across the top form the first part of the header control. Since this is a complete concept by itself, I created the links as a separate user control, a table with one data cell (Listing 15.7).

Listing 15.7 Defining the Table for the Links User Control
[View full width]
 <%@ Control Language="c#" AutoEventWireup="false" Codebehind="Links.ascx.cs" graphics/ccc.gif Inherits="softconcepts.Controls.Links" TargetSchema="http://schemas.microsoft.com/ graphics/ccc.gif intellisense/ie5" enableViewState="False"%> <TABLE>   <TR>     <TD>     </TD>   </TR> </TABLE> 

The first statement is the @ Control directive. Importantly, this directive associates the code-behind module with the visual part of the control. The table can be easily created by dropping an HTML table onto a new user control. The end result looks like a box on a user control.

The control directive information is coded for us if we use the designer to add the HTML table. We only need to modify this data if we code it manually or want to change something.

Creating Navigation Links

The next step is creating the actual navigation links. I could hard-code each link as individual cells with hyperlinks , but that would not be especially dynamic, and there is an easier way. By using an ASP.NET DataList control for the navigation links, I can reuse the Links control and dynamically change the content by binding a data source to the DataList .

To create links similar to those shown in Figure 15.7, follow these steps.

  1. Drop an ASP.NET DataList control from the Web Forms tab of the Toolbox into the single data cell of the table defined in the last subsection.

  2. Select the DataList on the user control and change the RepeatDirection property (in the Properties window) to Horizontal .

  3. Right-click the DataList to display the context menu and select Edit TemplatesItem Templates.

  4. In the ItemTemplate part of the template editor (Figure 15.10), add a space, an ASP.NET HyperLink control, and the pipe ( ) character. (You are defining the template. Every link will repeat this pattern.)

    Figure 15.10. Defining the template in the template editor.

    graphics/15fig10.gif

  5. Right-click and select End Template Editing from the context menu for the template editor. (Figure 15.11 shows the result to this point.)

    Figure 15.11. The finished design-time result after step 5.

    graphics/15fig11.gif

  6. The actual ToolTip property, the NavigateUrl property, and the display text are derived from a DataSource object. To complete this final part we need to modify the HTML directly and use a DataBinder object (Listing 15.8).

Listing 15.8 Creating a Dynamic Runtime View for the DataList Links
 1:  <%@ Control Language="vb" AutoEventWireup="false" 2:  Codebehind="Links.ascx.vb" Inherits="softconcepts.Controls.Links" 3:  TargetSchema=http://schemas.microsoft.com/intellisense/ie5 4:  enableViewState="False"%> 5:    <TABLE id="Table1" cellSpacing="0" cellPadding="0" 6:      width="100%" border="0"> 7:      <TR> 8:      <TD class="HeaderLinks" noWrap> 9:        <asp:datalist id="DataList1" 10:         RepeatDirection="Horizontal" runat="server"> 11:         <ItemStyle Wrap="False"></ItemStyle> 12:         <ItemTemplate> 13:           &nbsp; 14:           <asp:HyperLink id=HyperLink1 15:             runat="server" 16:             ToolTip='<%# DataBinder.Eval(Container.DataItem, 17:             "Description") %>' 18:             NavigateUrl='<%# DataBinder.Eval(Container.DataItem, 19:             "Url") + "?LinkID=" + 20:             DataBinder.Eval(Container.DataItem, "ID") %>' 21:             CssClass="HeaderLinks"> 22:             <%# DataBinder.Eval(Container.DataItem, "Name") %> 23:             </asp:HyperLink>&nbsp; 24:             <span class="HeaderLinks"></span> 25:         </ItemTemplate> 26:       </asp:datalist> 27:     </TD> 28:     </TR> 29:   </TABLE> 

The new information is comprised of the attributes on the <TABLE> and cell <TD> tags and the code between the <TD></TD> tags. (You can see this by comparing Listing 15.8 to Listing 15.7.)

I used the style HeaderLinks wherever styles were used. This style comes from the softconcepts.css style sheet we discussed earlier in the chapter. Most of the remaining modifications are contained in the <ItemTemplate> tag. This tag was added when I followed the numbered steps above.

Block script ( <%# %> ) blocks were used to bind the ToolTip property, the NavigateUrl property, and the display text values for the HyperLink control. For example, ToolTip is dynamically derived in lines 16 and 17 by using a script block and the DataBinder class's shared method Eval . The DataBinder class plays an intermediary role between a control that can be bound to data and the data itself.

 ToolTip='<%# DataBinder.Eval(Container.DataItem, "Description") %>' 

Understood in the simplest sense, the preceding excerpt means that HyperLink.ToolTip equals some derived value. DataBinder.Eval needs an object. The object is generically represented by the containing object's DataItem property. The fragment is understood to mean that there will be a hyperlink for each object in the data source, and that object will have a property named Description . Thus you should anticipate that the DataList control is bound to a DataSource object containing objects that have a Description property. The same approach is used for the NavigateUrl property, displayed text, and LinkID . To satisfy the DataBinder.Eval statements used, we would need a data source that contains objects with Description , ID , Name , and URL properties.

TIP

You can add a third parameter to DataBinder.Eval that provides a string formatting rule.

Listing 15.9 provides an example of implementing the Links class as a strongly typed collection.

Listing 15.9 Implementing the Links Class as a Strongly Typed Collection
 Public Class Links   Inherits System.Collections.ReadOnlyCollectionBase   Public Sub New()   End Sub   Public Shared Function HeaderLinks() As Links     Dim Links As Links = New Links()     Links.Add(New Link("Softconcepts home page", _       "Home", 1, "default.aspx"))     Links.Add(New Link("About softconcepts", _       "About", "2", "About.aspx"))     Return Links   End Function   Default Public ReadOnly Property Item( _     ByVal Index As Long) As Link   Get     Return CType(Innerlist(Index), Link)   End Get   End Property   Public Function Add(ByVal Value As Link) As Long     Return InnerList.Add(Value)   End Function End Class Public Class Link   Private FDescription As String   Private FID As String   Private FName As String   Private FUrl As String   Public Sub New(ByVal Description As String, _     ByVal Name As String, ByVal ID As String, ByVal Url As String)     Me.FDescription = Description     Me.FName = Name     Me.FID = ID     Me.FUrl = Url   End Sub   Public ReadOnly Property Description() As String   Get     Return FDescription   End Get   End Property   Public ReadOnly Property ID() As String   Get     Return FID   End Get   End Property   Public ReadOnly Property Name() As String   Get     Return FName   End Get   End Property   Public ReadOnly Property Url() As String   Get     Return FUrl   End Get   End Property End Class 

Listing 15.9 implements a strongly typed collection named Links . Links contains instances of the Link class, also defined in Listing 15.9. (For more information on strongly typed collections, refer to Chapter 14, which demonstrates and describes typed collections as return types for Web Services.) I could have implemented the links as a table in a database. Either way the code that consumes the link information is the same.

As you can see in the listingin the shared function HeaderLinks I implemented a factory method that creates an instance of the header links. There are only two links in the shared method. We just need to define the complete complement of Link objects and bind the return result to the DataList control. The DataBinder code in Listing 15.8 does the rest. The easiest way to bind a Link object to the data list is demonstrated in the Page_Load event handler for the Links.ascx user control (Listing 15.10). You can bind any object that implements IEnumerable , as does System.Collections.ReadOnlyCollectionBase (from which the Links class inherits).

Listing 15.10 Binding the Links Object
 Private Sub Page_Load(ByVal sender As System.Object, _   ByVal e As System.EventArgs) Handles MyBase.Load   DataList1.DataSource = Links.HeaderLinks   DataList1.DataBind() End Sub 

Adding a User Control to a Page

Adding the Links.ascx user control to a page is the easiest part of this whole process. All you have to do is drag the control from the Solution Explorer onto the page or another user control. In keeping with our concept of compositing, I actually implemented a Links user control and a Header user control. I wanted to reuse the Links control in another context, too: hyperlinks at the bottom of the page. Figure 15.12 shows the Header control as a work in-progress with the Links.ascx user control in place.

Figure 15.12. The Links.ascx user control as part of the composite Header user control.

graphics/15fig12.gif

Loading User Controls Dynamically

By creating user controls you have a lot of flexibility in how you orchestrate your user interface. You also have a lot of flexibility in when you orchestrate your user interface. A good friend of mine, Geoff Caylor, demonstrated this by using the CodeDOM and dynamically loaded controls.

The basic idea was to use the CodeDOM to generate some basic template classes that knew how to work with this particular dynamic page generator. Then, based on some state information in the application, these controls were loaded into a page at runtime. (A similar technique is used to make the IBuySpy portal application dynamic and flexible even after it has been deployed.) All we need to make this work is the LoadControl method, a control or user control, and somewhere to load the control into. The upcoming listing demonstrates how to load the Links.ascx user control into the Header.ascx user control at runtime instead of design time. But first we must do some preparation work.

Suppose we want to load the Links.ascx control into the top data cell of the HTML table at runtime. We need an object to refer to. By default, HTML controls don't have a reference in the code-behind file. However, if you make them server controls, you can refer to them in the code-behind file. This is actually very easy to dofollow these steps.

  1. Click on the top cell of the table in the Header control.

  2. In the Properties window, change the (id) property to LinksPane .

  3. Right-click on the data cell in the designer and select Run As Server Control (Figure 15.13).

    Figure 15.13. Select Run As Server Control to interact with HTML controls in the code-behind file.

    graphics/15fig13.gif

The last step adds a protected field named LinksPane in the code-behind file, and you will be able to refer to the HTML table cell with code at runtime. Listing 15.11 shows the code for dynamically loading the Links.ascx control into the Header control's LinksPane HTML table cell.

Listing 15.11 Loading Links.ascx at Runtime
 1:  Public MustInherit Class Header 2:    Inherits System.Web.UI.UserControl 3:    Protected WithEvents LinksPane As _ 4:      System.Web.UI.HtmlControls.HtmlTableCell 5: 6:    [ Web Form Designer generated code ] 7: 8:    Private Sub Page_Load(ByVal sender As System.Object, _ 9:      ByVal e As System.EventArgs) Handles MyBase.Load 10: 11:     LinksPane.Controls.Add(LoadControl("Links.ascx")) 12: 13:   End Sub 14: 15: End Class 

TIP

It might be a bit easier to follow Listing 15.11 if we declared a temporary variable for the LoadControl statement and then added that control to the LinksPane.Controls collection. However, as a general rule we want to avoid declaring unnecessary variables that add to the clutter and confusion of the code. New programmers may struggle a bit more with consolidated code.

The System.Web.UI.HtmlControls.HtmlTableCell control can be referred to in the code-behind file if we indicate that it is a server control. Line 11 loads the Links.ascx control and adds it to the LinksPane.Controls collection. The result is that the control appears in the header as if we had added it at design time. The benefit is that we could load something else if we desired, affording us a tremendous amount of flexibility.

You could borrow the concept of loading controls dynamically and generate an entire Web page at runtime. The pragmatics of doing this in a production application need some examination, but Listing 15.12 shows some code that creates a test page for the Links class defined in Listing 15.9. The test page can be used to ensure that the Links and Link classes are working correctly. Figure 15.14 shows the results of running the code in Listing 15.12.

Listing 15.12 Creating a Test Page for the Links Class
 1:  Public Class TestPage 2:      Inherits System.Web.UI.Page 3: 4:  [ Web Form Designer generated code ] 5: 6:    Private Sub Page_Load(ByVal sender As System.Object, _ 7:      ByVal e As System.EventArgs) Handles MyBase.Load 8: 9:      Dim DataGrid As DataGrid = New DataGrid() 10:     Controls.Add(DataGrid) 11:     DataGrid.Attributes.Add("width", "100%") 12:     DataGrid.DataSource = Links.HeaderLinks 13:     DataGrid.DataBind() 14: 15: 16:   End Sub 17: 18: End Class 
Figure 15.14. A dynamically created and bound DataGrid control.

graphics/15fig14.gif

The DataGrid control is created in line 9. There is nothing to prevent us from using the same technique to create individual controls like text boxes and labels or using LoadControl if we want to use user controls. The DataGrid control is added to the form's Controls collection. An attribute is set dynamically, to show an example of this technique in context. Finally, the DataGrid control's DataSource is provided, and the DataBind method is called. If we had added the DataGrid control at design time, we would have needed only the code in lines 12 and 13.

I like to use generated forms when I want to quickly test new objects. A generated form plays the role of scaffold , permitting me to focus on the object representing the business rulesin this case, Links without spending a lot of time on a user interface for testing.

Converting a Web Page into a User Control

In the simplest sense a user control is a Web page. You design user controls like Web pages. You load user controls by referring to the filename, and they contain the same kinds of things as Web pages. User controls contain other controls and are associated with code-behind files. As a matter of fact, if you create or have a page that you want to convert to a control, you can do so by stripping just a few of the tags out of the HTML and making modest modifications to the code-behind file. As an example, let's make a copy of the TestPage.aspx page and convert it to a user control. Follow the steps below. (The related listings are interspersed among the steps.)

  1. Copy the TestPage.aspx page to the Controls folder.

  2. Rename the file from TestPage.aspx to TestControl.ascx . ( .ascx is the extension convention for user control files.)

  3. The HTML for the page will look like the text in Listing 15.13. Delete all the text set in bold in the listing below.

    Listing 15.13 The TestPage.aspx HTML Code before Conversion to a User Control
     <%@ Page Language="vb" AutoEventWireup="false" Codebehind="TestPage.aspx.vb" Inherits="softconcepts_vb.TestPage"%>  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">   <HTML>   <HEAD>   <title>TestPage</title>   <meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">   <meta name="CODE_LANGUAGE" content="Visual Basic 7.0">   <meta name="vs_defaultClientScript" content="JavaScript">   <meta name="vs_targetSchema"   content="http://schemas.microsoft.com/intellisense/ie5">   </HEAD>   <body MS_POSITIONING="GridLayout">   <form id="Form1" method="post" runat="server">   </form>   </body>   </HTML>  
  4. Replace the @ Page directive with the @ Control directive.

  5. Change the Codebehind attribute to refer to TestControl.ascx. vb (be sure to change the original .aspx to .ascx ).

  6. Change the Inherits attribute to refer to softconcepts_vb.TestControl (to reflect the new class name we'll use for the control). Listing 15.14 shows the final result for the HTML. (Note that the TargetSchema code is added automatically.)

    Listing 15.14 The Converted Web Page Code, Now a User Control
     <%@ Control Language="vb" AutoEventWireup="false" Codebehind="TestControl.ascx.vb" Inherits="softconcepts_vb.TestControl" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> 
  7. In the TestControl.ascx.vb file, add the MustInherit modifier to the class declaration and change the Inherits statement to indicate that the control inherits from System.Web.UI.UserControl . Listing 15.15 shows the final result for the code-behind file.

    Listing 15.15 The Converted Code-Behind File, Now a User Control
     1:  Public MustInherit Class TestControl 2:    Inherits System.Web.UI.UserControl 3: 4:    [ Web Form Designer generated code ] 5: 6:    Private Sub Page_Load(ByVal sender As System.Object, _ 7:      ByVal e As System.EventArgs) Handles MyBase.Load 8: 9:      Dim DataGrid As DataGrid = New DataGrid() 10:     Controls.Add(DataGrid) 11:     DataGrid.Attributes.Add("width", "100%") 12:     DataGrid.DataSource = Links.HeaderLinks 13:     DataGrid.DataBind() 14: 15:   End Sub 16: 17: End Class 

Note that the only real revisions to the class were to the Class header and the Inherits clause. A UserControl class is defined as abstract; this is the meaning of the MustInherit modifier. The class now inherits from System.Web.UI.UserControl instead of System.Web.UI.Page . That's all you need to do.

If you want to convert a user control to a Web page, you can perform the same steps in reverse, or you could simply place the user control on a Web page. My guess is that more often than not you will want to convert a page into a control to promote reusability and to facilitate composite pages. ASP.NET applications that employ control compositing are likely to have as many, if not more, user controls than pages.



Visual Basic. NET Power Coding
Visual Basic(R) .NET Power Coding
ISBN: 0672324075
EAN: 2147483647
Year: 2005
Pages: 215
Authors: Paul Kimmel

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