8.5 Validation and Data Binding


Depending on the features of your custom controls, it may make sense to add support for either validation or data binding. This section describes how to build controls that support these two capabilities.

8.5.1 Supporting Validation

You can add validation support for custom controls that collect information from the user . For a control to support validation, it must be annotated with the ValidationProperty attribute, indicating which of its public properties should be tested with the validation algorithm, and when rendered in HTML, the value attribute of the rendered HTML must equate to the value that is to be validated (for client-side validation). Listing 8-36 shows a sample control to which validation controls could be applied, and Listing 8-37 shows a sample client page applying a validation control to our CustomTextBox control.

Listing 8-36 A Control That Supports Validation
 <ValidationProperty("Text")> _ Public Class CustomTextBox   Inherits Control   Public Sub New()     ViewState("Text") = ""   End Sub   Public Property Text As String     Get       Return CType(ViewState("Text"), String)     End Get     Set (value As String)       ViewState("Text") = value     End Set   End Property   Protected Overrides Sub Render(output As HtmlTextWriter)     output.AddAttribute(HtmlTextWriterAttribute.Name, _                         UniqueID)     output.AddAttribute(HtmlTextWriterAttribute.Value, _                         Text)     output.RenderBeginTag(HtmlTextWriterTag.Input)     output.RenderEndTag()   End Sub End Class 
Listing 8-37 Client to Custom Control Supporting Validation
 <%@ Page Language='VB' %> <%@ Register TagPrefix='eadn'     Namespace='EssentialAspDotNet.CustomControls'     Assembly='Validation' %> <html> <body> <form runat=server>   <eadn:ValidatableControl id='ctb' runat='server' />   <asp:RequiredFieldValidator runat='server'        ControlToValidate='ctb'>*   </asp:RequiredFieldValidator>   <input type='submit' value='submit' /> </form> </body> </html> 

8.5.2 Data-Bound Controls

Many controls in the WebControls hierarchy support the concept of data binding. You can add data binding support to your custom controls as well by exposing a DataSource property. When your control renders itself, it pulls data from the data source and uses it as part of its rendering.

To begin with, it is important to understand how clients expect data-bound controls to work. Typically, a client prepares a connection to a data source in the Load event of a Page class and connects that data source to the DataSource property that all data-bound controls provide. The data source could be a simple collection, such as an ArrayList ; a forward-only data reader, such as an IDataReader ; or a fully cached data source, such as a DataSet , DataView , or DataTable . As an author of data-bound controls, you need to accommodate all these possibilities. Because all the data-bound controls in the base class hierarchy retain their state across post-backs, clients expect any data-bound control you write to do the same, so that a control need be populated only the first time a page is accessed. Once the client has attached a data source to the control, she may need to set the DataTextField and DataValueField properties of the control to indicate which field from a tabular data source should be used. Note that if your control can render tabular data (rows with multiple columns ), there is no need to support these properties. Finally, once the data source, DataTextField , and DataValueField properties have been correctly populated, the client calls DataBind() , at which point your control's OnDataBinding method is called. Your control should iterate over the attached data source, saving the data locally within itself so that when your Render() method is called, you have the data to display. This interaction between a client page and a data-bound control is shown in Figure 8-5.

Figure 8-5. Interaction between a Page and a Data-Bound Control

graphics/08fig05.gif

8.5.3 Implementing a Data-Bound Control

The first step in creating a data-bound control is to define a DataSource property. This involves adding a public property called DataSource to your class of type object . Next you need to override the virtual function OnDataBinding , inherited from the Control base class. Your implementation of OnDataBinding should iterate across the data source that was assigned through your control's property, saving the data to a local data structure for future rendering. Note that it is not safe to assume that the data source will be around during your Render method, so you must take this step to ensure that you save the data that your control needs to be able to render itself during the OnDataBinding method call.

If your control expects only a list of items, not a tabular set of data, to be bound to it, you should expose another property, called DataTextField . This field will contain the index into a rowset that the client expects you to dereference when you perform the data binding. If you have the capability of associating values with the data items, you can also expose a property called DataValueField (most of the data-bound list controls in the base class hierarchy expose both of these fields). You should also be as accommodating as possible in what your control supports for data sources. All the data binding controls in the base class hierarchy support binding to any class that supports the IEnumerable or IList interface, plus they support binding directly to DataSets or DataTables by locating the default DataTable within a DataSet and the default DataView within a DataTable .

Clients of data-bound controls expect the controls to retain their state across post-backs. This lets page authors populate a control once if IsPostBack is false , and avoid additional round-trips to the database if the page is posted to again. As the author of a data bound control, you are responsible for making sure that your control's state is retained across a post-back. Typically, you do this by using the ViewState mechanism described earlier in this chapter. Because data-bound controls usually need to persist collections of data into view state, it is typically most efficient to override the LoadViewState and SaveViewState methods in your control to explicitly populate and retrieve collections of data from view state. Keep in mind that clients can always disable view state on your control, so even if you suspect it will be inefficient for clients to rely on state retention for your control, you should leave that decision to the client and support it nonetheless for consistency.

Listing 8-38 shows a sample control that supports data binding to all the different data source types with state retention. It renders itself as an item list and caches the contents of the data source in an ArrayList . This control defines two helper functions that should be useful for any implementer of data-bound controls. The first is GetResolvedDataSource , which takes the data source as an object and returns a reference to an IEnumerable interface. This function accounts for the fact that the data source may be a collection class, an IDataReader , a DataView , a DataTable , or a DataSet , and returns the enumeration interface on the correct element of the data source. The second helper function is GetDataItem , which takes an item pointed to by an enumerator and indexes it with the m_DataTextField value if is a rowset, or simply returns the object as a string if not. This is necessary to accommodate items stored in simple collections and items stored in tabular data sets.

Listing 8-38 A Data-Bound Control
 Public Class DataBoundControl   Inherits Control   Private _cachedData As ArrayList = new ArrayList()   Private _dataSource As Object   Private _dataTextField As String   Private _dataValueField As String   Public Property DataTextField As String     Get       Return _dataTextField     End Get     Set (value As String)       _dataTextField = value     End Set   End Property   Public Property DataValueField As String     Get       Return _dataValueField     End Get     Set (value As String)       _dataValueField = value     End Set   End Property   Public Overrides Property DataSource As Object     Get       Return _dataSource     End Get     Set (value As Object)       _dataSource = value     End Set   End Property   Public Function GetResolvedDataSource(ds As Object) _                   As IEnumerable     Dim dv As DataView     If ds Is IEnumerable Then       Return CType(ds, IEnumerable)     ElseIf ds is DataTable Then       dv = CType(ds, DataTable).DefaultView       Return CType(dv, IEnumerable)     ElseIf ds is DataSet Then       dv = CType(ds, DataSet).Tables(0).DefaultView       Return CType(dv, IEnumerable)     ElseIf ds is IList Then       Return CType(CType(ds, IList), IEnumerable)     Else       return Nothing     End If   End Function   Protected Function GetDataItem(item As Object) As String     Dim ret As String     If item Is DataRowView Then       DataRowView drv = CType(item, DataRowView)       ret = drv(_dataValueField).ToString();     ElseIf item Is DbDataRecord Then       Dim ddr As DbDataRecord = CType(item, DbDataRecord)       ret = ddr(_dataValueField).ToString()     Else       ret = item.ToString()     End If     Return ret   End Function   Protected Overrides Sub OnDataBinding(e As EventArgs)     MyBase.OnDataBinding(e)     If Not DataSource Is Nothing Then       Dim ds As IEnumerable       ds = GetResolvedDataSource(_dataSource)       Dim dataEnum As IEnumerator       dataEnum = ds.GetEnumerator()       Do While dataEnum.MoveNext()         _cachedData.Add(GetDataItem(dataEnum.Current))       Loop     End If   End Sub   Protected Overrides Sub Render(htw As HtmlTextWriter)     htw.RenderBeginTag(HtmlTextWriterTag.Ul) '<ul>     Dim s As String     For Each s in _cachedData       htw.RenderBeginTag(HtmlTextWriterTag.Li) '<li>       htw.Write(s)       htw.RenderEndTag() '</li>     Next s     htw.RenderEndTag() '</ul>   End Sub   Protected Overrides Sub LoadViewState( _                                   savedState As Object)     If Not savedState Is Nothing       ' Load State from the array of objects that       ' was saved in SaveViewState       Dim vState As Object() = CType(savedState, Object())       If Not vState(0) Is Nothing Then         MyBase.LoadViewState(vState(0))       End If       If Not vState(1) Is Nothing Then         _cachedData = CType(vState(1), ArrayList)       End If     End If   End Sub   Protected Overrides Function SaveViewState() As Object     Dim vState(2) As Object     vState(0) = MyBase.SaveViewState()     vState(1) = _cachedData     Return vState   End Function End Class 


Essential ASP.NET with Examples in Visual Basic .NET
Essential ASP.NET with Examples in Visual Basic .NET
ISBN: 0201760398
EAN: 2147483647
Year: 2003
Pages: 94
Authors: Fritz Onion

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