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 : Control {   public CustomTextBox()   { ViewState["Text"] = ""; }   public string Text   {     get { return (string) ViewState["Text"]; }     set { ViewState["Text"] = value;         }   }   protected override void Render(HtmlTextWriter output)   {     output.AddAttribute(HtmlTextWriterAttribute.Name,                         UniqueID);     output.AddAttribute(HtmlTextWriterAttribute.Value,                         Text);     output.RenderBeginTag(HtmlTextWriterTag.Input);     output.RenderEndTag();   } } 
Listing 8-37 Client to Custom Control Supporting Validation
 <%@ Page Language='C#' %> <%@ 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 Data Table 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 : Control {   private ArrayList _cachedData = new ArrayList();   private object _dataSource;   private string _dataTextField;   private string _dataValueField;   public string DataTextField   {     get { return _dataTextField;  }     set { _dataTextField = value; }   }   public string DataValueField   {     get { return _dataValueField;  }     set { _dataValueField = value; }   }   public override object DataSource   {     get {return _dataSource;}     set {_dataSource = value;}   }   public IEnumerable GetResolvedDataSource(object ds)   {     if (ds is IEnumerable)       return (IEnumerable)ds;     else if (ds is DataTable)       return (IEnumerable)(((DataTable)ds).DefaultView);     else if (ds is DataSet)     {       DataView dv = ((DataSet)ds).Tables[0].DefaultView;       return (IEnumerable)dv;     }     else if (ds is IList)       return (IEnumerable)((IList)ds);     else       return null;   }   protected string GetDataItem(object item)   {     string ret;     if (item is DataRowView)     {       DataRowView drv = (DataRowView)item;       ret = drv[_dataValueField].ToString();     }     else if (item is DbDataRecord)     {       DbDataRecord ddr = (DbDataRecord)item;       ret = ddr[_dataValueField].ToString();     }     else       ret = item.ToString();     return ret;   }   protected override void OnDataBinding(EventArgs e)   {     base.OnDataBinding(e);     if (DataSource != null)     {       IEnumerable ds = GetResolvedDataSource(_dataSource);       IEnumerator dataEnum = ds.GetEnumerator();       while (dataEnum.MoveNext())         _cachedData.Add(GetDataItem(dataEnum.Current));     }   }   protected override void Render(HtmlTextWriter htw)   {     htw.RenderBeginTag(HtmlTextWriterTag.Ul); // <ul>     foreach (string s in _cachedData)     {       htw.RenderBeginTag(HtmlTextWriterTag.Li); // <li>       htw.Write(s);       htw.RenderEndTag(); // </li>     }     htw.RenderEndTag(); // </ul>   }   protected override void LoadViewState(object savedState)   {     if (savedState != null)     {       // Load State from the array of objects that       // was saved in SaveViewState       object[] vState = (object[])savedState;       if (vState[0] != null)         base.LoadViewState(vState[0]);       if (vState[1] != null)         _cachedData = (ArrayList)vState[1];     }   }   protected override object SaveViewState()   {     object[] vState = new object[2];     vState[0] = base.SaveViewState();     vState[1] = _cachedData;     return vState;   } } 


Essential ASP.NET With Examples in C#
Essential ASP.NET With Examples in C#
ISBN: 0201760401
EAN: 2147483647
Year: 2003
Pages: 94
Authors: Fritz Onion

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