|
Essential ASP.NET with Examples in Visual Basic .NET Authors: Onion F. Published year: 2003 Pages: 75/94 |
8.5 Validation and Data BindingDepending 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 ValidationYou 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 ControlsMany 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
8.5.3 Implementing a Data-Bound ControlThe 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 Authors: Onion F. Published year: 2003 Pages: 75/94 |