Creating Templated Databound Controls


In this section, you learn how to build templated databound controls. A databound control can be bound to a DataSource control such as the SqlDataSource or ObjectDataSource controls.

The ASP.NET 2.0 Framework provides you with a number of new base classes that you can use when creating a custom databound control. Creating a databound control using the previous version of the Framework was not a trivial matter (it took longer than a single afternoon). The ASP.NET 2.0 Framework, on the other hand, makes creating databound controls easy. (You can implement a custom databound control in less than 15 minutes.)

So, let's look at some tables and figures. Table 33.1 lists the base control classes for all the standard ASP.NET databound controls. Figure 33.4 displays the inheritance hierarchy of all the new databound controls in the ASP.NET 2.0 Framework. Typically, you'll inherit from one of the leaf nodes. You'll create a control that derives from the base CompositeDataBoundControl, HierarchicalDataBoundControl, or ListControl class.

Table 33.1. Base Databound Control Classes

Control

Base Control

GridView, DetailsView, FormView

CompositeDataBoundControl

Menu, treeView

HierarchicalDataBoundControl

DropDownList, ListBox, RadioButtonList, CheckBoxList, BulletedList

ListControl

DataList, DataGrid

BaseDataList

Repeater

Control


Figure 33.4. Databound control inheritance hierarchy.


This chapter concentrates on inheriting new controls from the base CompositeDataBoundControl class. This is the appropriate base class to use when you want to display one or more database records and use templates.

Note

You learned how to create controls that inherit from the base ListControl class in Chapter 10, "Using List Controls."


Creating a DivView Control

Let's start simple. In this section, we create a custom databound control named the DivView control. The DivView control displays a set of data items (database records) in HTML <div> tags.

The DivView control inherits from the base CompositeDataBoundControl class and overrides a single method of the base class. The DivView control overrides the base class's CreateChildControls() method.

The DivView control is contained in Listing 33.9.

Listing 33.9. DivView.vb

[View full width]

Imports System Imports System.Collections Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace AspNetUnleashed     Public Class DivView         Inherits CompositeDataBoundControl         Private _itemTemplate As ITemplate         <TemplateContainer(GetType(DivViewItem))> _         <PersistenceMode(PersistenceMode.InnerProperty)> _         Public Property ItemTemplate() As ITemplate             Get                 Return _itemTemplate             End Get             Set(ByVal Value As ITemplate)                 _itemTemplate = Value             End Set         End Property         Protected Overrides Function CreateChildControls(ByVal dataSource As IEnumerable,  ByVal dataBinding As Boolean) As Integer             Dim counter As Integer = 0             For Each dataItem As Object In dataSource                 Dim contentItem As New DivViewItem(dataItem, counter)                 _itemTemplate.InstantiateIn(contentItem)                 Controls.Add(contentItem)                 counter = counter + 1             Next             DataBind(False)             Return counter         End Function         Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag             Get                 Return HtmlTextWriterTag.Div             End Get         End Property     End Class     Public Class DivViewItem         Inherits WebControl         Implements IDataItemContainer         Private _dataItem As Object         Private _index As Integer         Public ReadOnly Property DataItem() As Object Implements IDataItemContainer.DataItem             Get                 Return _dataItem             End Get         End Property         Public ReadOnly Property DataItemIndex() As Integer Implements IDataItemContainer .DataItemIndex             Get                 Return _index             End Get         End Property         Public ReadOnly Property DisplayIndex() As Integer Implements IDataItemContainer .DisplayIndex             Get                 Return _index             End Get         End Property         Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag             Get                 Return HtmlTextWriterTag.Div             End Get         End Property         Public Sub New(ByVal dataItem As Object, ByVal index As Integer)             _dataItem = dataItem             _index = index         End Sub     End Class End Namespace 

The DivView control supports an ItemTemplate that is used to format each of its data items. You are required to supply an ItemTemplate when you use the DivView control.

All the work happens in the CreateChildControls() method. Notice that this is not the same CreateChildControls() method that is included in the base System.Web.UI.Control class. The DivView control overrides the CompositeDataBounControl's CreateChildControls() method.

The CreateChildControls() method accepts the following two parameters:

  • dataSource Represents all the data items from the data source.

  • dataBinding Represents whether or not the CreateChildControls() method is called when the data items are being retrieved from the data source.

The CreateChildControls() method is called every time that the DivView control renders its data items. When the control is first bound to a DataSource control, the dataSource parameter represents the data items retrieved from the DataSource control. After a postback, the dataSource parameter contains a collection of null values, but the correct number of null values.

After a postback, the contents of the data items can be retrieved from View State. As long as the correct number of child controls is created, the Framework can rebuild the contents of the databound control.

You can use the dataBinding parameter to determine whether the data items from the data source actually represent anything. Typically, the dataBinding parameter has the value TRue when the page first loads, and the value False after each postback.

Notice that the DataBind() method is called after the child controls are created. You must call the DataBind() method when a template includes databinding expressions. Otherwise, the databinding expressions are never evaluated.

The page in Listing 33.10 illustrates how you can bind the DivView control to a SqlDataSource control.

Listing 33.10. ShowDivView.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="AspNetUnleashed" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         .movies         {             width:500px;         }         .movies div         {             border:solid 1px black;             padding:10px;             margin:10px;         }     </style>     <title>Show DivView</title> </head> <body>     <form  runat="server">     <div>     <custom:DivView                  DataSource         Css         Runat="Server">         <ItemTemplate>         <h1><%# Eval("Title") %></h1>         Director: <%# Eval("Director") %>         </ItemTemplate>     </custom:DivView>     <asp:SqlDataSource                  ConnectionString="<%$ ConnectionStrings:Movies %>"         SelectCommand="SELECT Title, Director FROM Movies"         Runat="server" />     <br />     <asp:LinkButton                  Text="Reload"         Runat="server" />     </div>     </form> </body> </html> 

In Listing 33.10, the SqlDataSource control represents the Movies database table. The DivView control includes an EditItemTemplate that formats each of the columns from this database table (see Figure 33.5).

Figure 33.5. Displaying database records with the DivView control.


Creating an AjaxDivView Control

Two of the three ASP.NET controls that derive from the CompositeDataBoundControl class take advantage of AJAX. The GridView control supports AJAX when sorting and paging through database records. The DetailsView control supports AJAX when paging through database records.

There is a good reason for supporting AJAX when building databound controls. Using AJAX can significantly improve your application's performance. By taking advantage of AJAX, you can avoid re-creating an entire page each and every time you perform a database operation. AJAX enables you to transfer only the information you need back and forth between the web server and browser.

Note

Microsoft insists on using the term Client Callbacks to refer to AJAX. Because AJAX has become the standard name for this technology, I use AJAX instead of Client Callbacks in this book.


In this section, we add AJAX functionality to the DivView control. The modified DivView control, the AjaxDivView control, supports having its contents refreshed through an AJAX call.

The AjaxDivView control is contained in Listing 33.11.

Listing 33.11. AjaxDivView.vb

[View full width]

Imports System Imports System.Text Imports System.IO Imports System.Collections Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace AspNetUnleashed     Public Class AjaxDivView         Inherits CompositeDataBoundControl         Implements ICallbackEventHandler         Private _itemTemplate As ITemplate         ''' <summary>         ''' The ItemTemplate is used to format each item         ''' from the data source         ''' </summary>         <TemplateContainer(GetType(DivViewItem))> _         <PersistenceMode(PersistenceMode.InnerProperty)> _         Public Property ItemTemplate() As ITemplate             Get                 Return _itemTemplate             End Get             Set(ByVal Value As ITemplate)                 _itemTemplate = Value             End Set         End Property         ''' <summary>         ''' Register JavaScripts         ''' </summary>         Protected Overrides Sub OnPreRender(ByVal e As EventArgs)             ' Register JavaScript library             Page.ClientScript.RegisterClientScriptInclude("AjaxDivView",Page.ResolveUrl("~ /ClientScripts/AjaxDivView.js"))             ' Register Refresh function             Dim eRef As String = Page.ClientScript.GetCallbackEventReference(Me, Nothing,  "AjaxDivView_Result", "'" & Me.ClientID & "'", "AjaxDivView_Error", False)             Dim refreshFunc As String = "function AjaxDivView_Refresh() {" & eRef & "}"             Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), Me.UniqueID,  refreshFunc, True)             MyBase.OnPreRender(e)         End Sub         ''' <summary>         ''' Iterate through the data items and instantiate each data         ''' item in a template         ''' </summary>         Protected Overrides Function CreateChildControls(ByVal dataSource As IEnumerable,  ByVal dataBinding As Boolean) As Integer             Dim counter As Integer = 0             For Each dataItem As Object In dataSource                 Dim contentItem As New DivViewItem(dataItem, counter)                 _itemTemplate.InstantiateIn(contentItem)                 Controls.Add(contentItem)                 counter = counter + 1             Next             DataBind(False)             Return counter         End Function         ''' <summary>         ''' Render this control's contents in a DIV tag         ''' </summary>         Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag             Get                 Return HtmlTextWriterTag.Div             End Get         End Property         ''' <summary>         ''' Whenever I get called through AJAX,         ''' rebind my data         ''' </summary>         Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements  ICallbackEventHandler.RaiseCallbackEvent             Me.DataBind()         End Sub         ''' <summary>         ''' Render my contents to a string         ''' and send the result back to the client         ''' </summary>         Public Function GetCallbackResult() As String Implements ICallbackEventHandler .GetCallbackResult             Dim builder As New StringBuilder()             Dim sWriter As New StringWriter(builder)             Dim hWriter As New HtmlTextWriter(sWriter)             Me.RenderContents(hWriter)             Return builder.ToString()         End Function     End Class     Public Class AjaxDivViewItem         Inherits WebControl         Implements IDataItemContainer         Private _dataItem As Object         Private _index As Integer         Public ReadOnly Property DataItem() As Object Implements IDataItemContainer.DataItem             Get                 Return _dataItem             End Get         End Property         Public ReadOnly Property DataItemIndex() As Integer Implements IDataItemContainer .DataItemIndex             Get                 Return _index             End Get         End Property         Public ReadOnly Property DisplayIndex() As Integer Implements IDataItemContainer .DisplayIndex             Get                 Return _index             End Get         End Property         Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag             Get                 Return HtmlTextWriterTag.Div             End Get         End Property         Public Sub New(ByVal dataItem As Object, ByVal index As Integer)             _dataItem = dataItem             _index = index         End Sub     End Class End Namespace 

The AjaxDivView control in Listing 33.11 is very similar to the DivView control created in the previous section, except for the fact that it implements the ICallbackEventHandler interface. This interface has two methods that you must implement: the RaiseCallbackEvent() and GetCallbackResult() methods.

The AjaxDivView control calls its DataBind() method in the RaiseCallbackEvent() method. When an AJAX call is made to this control, the control automatically refreshes its contents by rebinding to its data source.

The GetCallbackResult() returns a string that is sent to the browser as a result of an AJAX call. The AjaxDivView control renders its contents to a string and sends the string to the browser.

Notice that the AjaxDivView control registers two JavaScript scripts in its OnPreRender() method. First, the method registers an external JavaScript library named AjaxDivView.js. Second, it creates a JavaScipt function named AjaxDivView_Refresh() that refreshes the contents of the AjaxDivView.

The AjaxDivView.js file is contained in Listing 33.12.

Listing 33.12. AjaxDivView.js

function AjaxDivView_Result(result, controlID) {     var control = document.getElementById(controlID);     control.innerHTML = result; } function AjaxDivView_Error(error) {     alert( error ); } 

The JavaScript file in Listing 33.12 includes two functions. The first function, named AjaxDivView_Result(), is called after AjaxDivView content is retrieved from the server. This function updates the innerHTML of the AjaxDivView control's containing <div> tag with the updated content.

The second function, the AjaxDivView_Error() function, is called only when there is an error on the server during an AJAX call. This function simply displays the error in a JavaScript alert box.

The page in Listing 33.13 illustrates how you can use the AjaxDivView control.

Listing 33.13. ShowAjaxDivView.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="AspNetUnleashed" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <script type="text/javascript">     window.onload = function()     {         window.setInterval("AjaxDivView_Refresh()", 5000);     }     </script>     <style type="text/css">         h1         {             font-size:16px;         }         .ajaxDivView div         {             border:solid 1px black;             padding:5px;             margin:10px;         }     </style>     <title>Show AjaxDivView</title> </head> <body language="javascript">     <form  runat="server">     <div>     Page Time: <%= DateTime.Now.ToString() %>     <br /><br />     <button onclick="AjaxDivView_Refresh();return false;">Refresh</button>     <br /><br />     <custom:AjaxDivView                  DataSource         Css         Runat="server">         <ItemTemplate>         <h1><%# Eval("Title") %></h1>         <em>Director: <%# Eval("Director") %></em>         </ItemTemplate>     </custom:AjaxDivView>     <asp:SqlDataSource                  ConnectionString="<%$ ConnectionStrings:Movies %>"         SelectCommand="SELECT Id,Title,Director FROM Movies"         Runat="server" />     </div>     </form> </body> </html> 

The page in Listing 33.13 displays the contents of the Movies database table. The AjaxDivView control is bound to a SqlDataSource control.

The page includes a little bit of JavaScript. The JavaScript causes the contents of the AjaxDivView to refresh automatically every five seconds. If you change one of the records in the Movies database table, then the contents of the AjaxDivView control automatically updates to reflect the change within five seconds (see Figure 33.6).

Figure 33.6. Displaying data with the AjaxDivView control.


The following JavaScript statement causes the AjaxDivView to update its contents every five seconds:

window.setInterval("AjaxDivView_Refresh()", 5000); 


The AjaxDivView_Refresh() method initiates the AJAX call to refresh the AjaxDivView control's contents.

As an alternative to waiting the five seconds after a database update, if you are really impatient, you can click the button contained on the page. The button is a client-side button that calls the AjaxDivView_Refresh() method in its onclick handler.

Notice that the current time is displayed at the top of the page. This time never changes even when the contents of the AjaxDivView is updated. Only the AjaxDivView is refreshed, and not the rest of the page.

Creating an AjaxFormView Control

In this section, we create an AjaxFormView control. The AjaxFormView control works like the standard ASP.NET FormView control. However, the AjaxFormView control enables you to update and delete database records from the client by making AJAX calls back to the server.

Unfortunately, the code for the AjaxFormView control is too long to include in the pages of this book. The entire source code for the control is included on the CD that accompanies this book in both VB.NET and C# versions.

The AjaxFormView control has the following properties:

  • InsertItemTemplate Enables you to supply a template that is used when the control is in Insert mode.

  • EditItemTemplae Enables you to supply a template that is used when the control is in Edit mode.

  • DataKeyNames Enables you to specify one or more primary keys to use when updating a record.

  • DataKey Enables you to retrieve the value of the data key associated with the current record being edited.

  • DefaultMode Enables you to place the control in either Edit or Insert mode.

  • OnClientItemInserted Enables you to specify an optional script that executes when a new record is inserted.

  • OnClientItemUpdated Enables you to specify an optional script that executes when an existing record is updated.

The AjaxFormView control can be used to either insert or update a database record. You can set the control to either Insert or Update mode by setting the DefaultMode property.

Warning

Make sure that you assign a value to the AjaxFormView control's DataKeyNames property when the control is set to Edit mode.


When you set the AjaxFormView control to Insert mode, the control renders an Insert button with its RenderInsertButton() method. This method creates a client-side button that initiates an AJAX call. The code for the RenderInsertButton() method is contained in Listing 33.14.

Listing 33.14. RenderInsertButton()

[View full width]

''' <summary> ''' Render the Insert button with the AJAX onclick handler ''' </summary> Private Sub RenderInsertButton(ByVal writer As HtmlTextWriter)   Dim eRef As String = Page.ClientScript.GetCallbackEventReference(Me, "'insert'",  _onClientItemInserted, "'" & Me.ClientID & "'", "AjaxFormView_Error", False)   eRef = "__theFormPostData='';WebForm_InitCallback();" & eRef & ";return false"   writer.AddAttribute(HtmlTextWriterAttribute.Onclick, eRef)   writer.RenderBeginTag(HtmlTextWriterTag.Button)   writer.Write("Insert")   writer.RenderEndTag() End Sub 

The RenderInsertButton() method creates a button onclick handler that performs three actions. First, it sets the __theFormPostData variable to an empty string. Next, it calls the WebForm_InitCallback() method. These first two steps are required to pass the updated form field values back to the server in the AJAX call. By default, when an AJAX call is made back to the server, only the initial values of all the form fields are sent with the AJAX request.

Finally, the onclick handler actually makes the AJAX call. The necessary function call to initiate the AJAX request is retrieved with the help of the Page.ClientScript.GetCallbackEventReference() method.

When a user clicks the Insert button, an AJAX call is made back to the server and the control's RaiseCallbackEvent() method executes. When the Insert button is clicked, this method calls the HandleInsert() method. The code for the HandleInsert() method is contained in Listing 33.15.

Listing 33.15. HandleInsert()

''' <summary> ''' Perform database insert by executing DataSource Insert method ''' </summary> Private Sub HandleInsert()   Dim values As IOrderedDictionary = _insertItemTemplate.ExtractValues(_item)   Dim dataSource As DataSourceView = CType(Me.GetData(), DataSourceView)   dataSource.Insert(values, AddressOf DataCallback) End Sub 

The HandleInsert() method gets the values of the databinding expressions from the InsertItemTemplate by calling the template's ExtractValues() method. This method is available because the InsertItemTemplate is marked as a template that supports two-way databinding.

The DataSource control to which the AjaxFormView control is bound has a DataSourceView associated with it. This DataSourceView is retrieved by a call to the GeTData() method. The DataSourceView class includes Insert(), Update(), and Delete() methods you can call to modify the database data associated with the FormView. The HandleInsert() method in Listing 33.15 calls the Insert() method to insert a new database record.

The page in Listing 33.16 illustrates how you can use the AjaxFormView control. This page contains three controls: an AjaxFormView, AjaxDivView, and SqlDataSource control. The page enables you to add new records to the Movies database table (see Figure 33.7).

Figure 33.7. Inserting new records with the AjaxFormView control.


Listing 33.16. ShowAjaxFormView.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="AspNetUnleashed" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <script type="text/javascript">         function ItemInserted(error, controlID)         {             if (error == '')             {                 var ajaxFormView = document.getElementById(controlID);                 var inputs = ajaxFormView.getElementsByTagName('input');                 for (var i=0;i < inputs.length;i++)                     inputs?.value = inputs?.defaultValue;                 window.setTimeout("AjaxDivView_Refresh()", 100);             }             else                 alert( 'Error: ' + error );         }     </script>     <style type="text/css">         html         {             background-color:silver;         }         .headerStrip         {             margin-bottom:10px;             padding:10px;             background-color:white;             border:solid 1px black;         }         .frmMovie         {             background-color:white;             width:400px;             padding:10px;             margin-right:20px;             float:left;             border:solid 1px black;         }         .listMovies         {             background-color:white;             float:left;             padding:10px;             border:solid 1px black;         }         .listMovies div         {             margin:10px;             border:solid 1px black;             padding:5px;         }     </style>     <title>Show AjaxFormView</title> </head> <body>     <form  runat="server">     <div >     Page Time: <%= DateTime.Now.ToString("T") %>     </div>     <custom:AjaxFormView                  DataSource         DefaultMode="insert"         OnClientItemInserted="ItemInserted"         Css         Runat="server">         <InsertItemTemplate>         <asp:Label                          Text="Movie Title:"             AssociatedControl             Runat="server" />         <br />         <asp:TextBox                          Text='<%# Bind("Title") %>'             Runat="server" />         <br /><br />         <asp:Label                          Text="Movie Director:"             AssociatedControl             Runat="server" />         <br />         <asp:TextBox                          Text='<%# Bind("Director") %>'             Runat="server" />         </InsertItemTemplate>     </custom:AjaxFormView>     <custom:AjaxDivView                  DataSource         Css         Runat="server">         <ItemTemplate>         <h1><%# Eval("Title") %></h1>         <em>Director:<%# Eval("Director") %></em>         </ItemTemplate>     </custom:AjaxDivView>     <asp:SqlDataSource                  ConnectionString="<%$ ConnectionStrings:Movies %>"         SelectCommand="SELECT Id,Title, Director             FROM Movies ORDER BY Id DESC"         InsertCommand="INSERT MOVIES (Title, Director)             VALUES (@Title, @Director)"         Runat="server" />     </div>     </form> </body> </html> 

When you add new records to the Movies database table by clicking the AjaxFormView control's Insert button, the page is not posted back to the server. Instead, the new record is added to the database with an AJAX call.

The AjaxDivView displays the current contents of the Movies database table. The contents of the AjaxDivView control are refreshed automatically whenever a new record is added with the AjaxFormView control. The AjaxDivView control is refreshed by the AjaxFormView control's client-side ItemInserted() method.

The beautiful thing about both the AjaxFormView and AjaxDivView control is that both controls enable you to interact with the web server without posting the page that contains the controls back to the web server. This creates a user experience much closer to working with a desktop application. It also greatly improves the performance of your application because the entire page does not need to be re-created each and every time that you need to perform a database operation.




ASP. NET 2.0 Unleashed
ASP.NET 2.0 Unleashed
ISBN: 0672328232
EAN: 2147483647
Year: 2006
Pages: 276

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