DataBound


ASP.NET databound controls include the ListView, DetailsView, GridView, DataList, Repeater, and DropDownList among others.  In addition to these controls you can create your own databound controls.  To begin with, let’s recall the valid data sources for a databound control:

VALID DATA SOURCES - The data source for databound controls can be either of the following types:

1) Enumerable collections (use the DataSource property to bind to collections)

Classes that implement the IListSource or IEnumerable interfaces

2) Data source controls (use the DataSourceID property to bind to data source controls)

Controls that implement the IDataSource interface (for example: SqlDataSource, ObjectDataSource, XmlDataSource and AccessDataSource)

The DataBoundControl class is the base class for all DataBound controls.  It’s a member of the System.Web assembly and lives in the System.Web.UI.WebControls namespace.

image

It inherits from the BaseDataBoundControl class:

image

The BaseDataBoundControl exposes the following properties:

DataSource – Allows binding to enumerable collections

DataSourceID – Allows binding to data source controls

IsBoundUsingDataSourceID – Checks if the DataSourceID property is set.  If so, it returns true.  Use this property to determine what type of datasource is being set.  If true then your datasource is a data source control.  If false, then your datasource is a collection.

The BaseDataBoundControl has two abstract methods overriden by the DataBoundControl: 

PerformSelect – retrieves data from a data source

ValidateDataSource – Checks that the data source is valid. 

The DataBoundControl class has the following Properties:

DataMember - The DataMember property specifies which table to bind to when there are multiple tables in a DataSet (by default, the first table in the  DataSet is used).

The DataBoundControl class has the following Methods:

PerformDataBinding – Use this when the datasource is an enumerable collection.  The collection is retrieved using the PerformSelect method which in turn checks the IsBoundUsingDataSourceID to see if its false, and if so then it knows it’s a collection.

MarkAsDataBound – Sets a value in view state to “true” to specify that the control is already bound to data.

GetData – Returns a DataSourceView object (a list of data).  DataSourceView objects provide a “Select” method which is used to retrieve the actual data.

Anyway, here is how to create a custom databound control.  In this example I will create a simple control that allows you to specify a specific column.  then the control will display the contents of the column in an html table.

1) Create a New Project in Visual Studio and select Visual C# / Web in the Project Types.  Then select the ASP.NET Server Control template.  For now, delete everything except for the namespace.  I decided to rename the cs file to MyDataBoundControl.cs

2) Create a class that derives from the DataBoundControl class.  Make sure you have a reference for System.Web in your project and a using directive for the System.Web.UI.WebControls, System.Collections namespaces in your code:

using System;

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyCustomControls
{
    public class MyDataBoundControl : DataBoundControl
    {

    }
}

3) Declare a public property to specify what your control will bind to.  Make sure you call the OnDataPropertyChanged method (of the DataBoundControl class in the Setter so that the control rebinds when the property value changes.  Wrap it up in an if statement to check if the control has already been initialized.  If so, then go ahead and call the OndataPropertyChanged method.  Also make sure to store the value of the property in a ViewState object.

In this case we will declare a property called “DataTextField” to get / set the field of the data source that we want to display.  This will provide similar functionality as the existing Dropdownlist.DataTextField and the ListControl.DataTextField properties.

public string DataTextField
{
    get
    {

         //retrieve the value from the ViewState
        object o = this.ViewState["DataTextField"];

        //if the object is null return string.Empty else return the object casted to string
        return ((o == null) ? string.Empty : (string)o);
    }
    set
    {

       //Deposit the value into ViewState
        this.ViewState["DataTextField"] = value;

       //If the object is already initialized that means the object value is changing
       //therefore we call the OnDataPropertyChanged to perform a rebind

        if (Initialized)
        {
            OnDataPropertyChanged();
         }
    }
}

4) Override the PerformSelect method.  Check the IsBoundUsingDataSourceID property.  If it’s set true, the DataSourceID was used.  If it’s set to false, the DataSource was used.  If the DataSource was used, then call the OnDataBinding method to raise the DataBinding event.  The OnDataBinding method can be overriden by the client application to perform any custom logic for binding data.  Then call the GetData method to retrieve the DataSourceView (list of data) and call its Select method.  The DataSourceView Select method calls the ExecuteSelect method of the DataSourceView.  It takes two parameters:  The DataSourceSelectArguments and the DataSourceViewSelectCallback (a delegate to notify that the ExecuteSelect operation has completed.  The ExecuteSelect statements returns its data in the ienumerable parameter of this delegate).  You can create a method for this delegate to perform any custom processing of the data.

protected override void PerformSelect()
{
    if (!IsBoundUsingDataSourceID)
    {
        this.OnDataBinding(EventArgs.Empty);
    }


    GetData().Select(CreateDataSourceSelectArguments(),
        this.MySelectCallback);

    // The PerformDataBinding method has completed.
    RequiresDataBinding = false;
    MarkAsDataBound();

    // Raise the DataBound event.
    OnDataBound(EventArgs.Empty);
}

5) Here is the method that will be passed to the Select delegate argument in order to process the data returned from the GetData().Select method.  Notice that we are once again checking the IsBoundUsingdataSourceId property.  If its set to false, we already called the OnDataBinding before calling the GetData method.  If it’s set to true, then we still haven’t called the OnDataBinding method and we need to call it now.  Then call the PerformDataBinding method that we override from the DataBoundControl class.

private void MySelectCallback(IEnumerable myReturnedData)
{
    
   if (IsBoundUsingDataSourceID)
    {
        OnDataBinding(EventArgs.Empty);
    }

    PerformDataBinding(myReturnedData);
}

6) Override the PerformDataBinding method of the DataBoundControl class.  Call the base.PerformDataBinding first passing the input ienumerable parameter.  Then you can add your custom functionality to the method.  Test the ienumerable input parameter for null.  If it’s not null then process the data in the ienumerable.  Check our DataTextField property for null or empty.

protected override void PerformDataBinding(IEnumerable myRetrievedData)
{

base.PerformDataBinding(myRetrievedData);

if (myRetrievedData!= null)
{

    string strDataTextField = String.Empty;
    if (string.IsNullOrEmpty(this.DataTextField) == false)

    {
        strDataTextField = this.DataTextField;
    }

}

}

7) Note the namespace and class name of your control:

namespace MyCustomControlNamespace
{
    public class MyDataBoundControl : DataBoundControl
    {

8) Create a tester web project to test your control.  Register your custom control in the tester’s webconfig file:

    <pages>
        <controls>
            <add tagPrefix="aspSample" namespace="MyCustomControlNamespace" assembly="MyCustomControlNamespace"/>
        </controls>
    </pages>

9) Compile the solution of your custom control.  In windows explorer, find the assembly that was generated in your custom control’s solution bin directory.  Copy the assembly into the bin folder of your tester web project.  Then, in your tester web project, add a reference to your custom control assembly in the bin directory.  repeat this every time you make a change  to your custom control.

10) Compile your tester web project.  Then create a web page and add a SqlDataSource to test your custom control:

<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:AdventureWorksConnectionString %>"                               
    SelectCommand="select * from HumanResources.Employee">                           
</asp:SqlDataSource>

11) Add markup to include your control in the web page.  Set the DataTextField and DataSourceID properties


     <aspsample:MyDataBoundControl id="MyDataBoundControl1" runat="server"
       DataTextField="VacationHours" DataSourceID="SqlDataSource1">
     </aspsample:MyDataBoundControl >

That’s it.  Here is what the testing looks like:

1) Starting out in the web page’s code-behind so we can step into the custom control’s code:

image

2) The PerformSelect is the first method that gets invoked.  It checks the IsBoundUsingDataSourceID property which in this case is set to true (because we assigned it a SqlDataSource control to it’s DataSourceID property.)  So the OnDataBinding is not called here.

image

3) Then it calls the GetData method’s DataSourceView’s Select statement:

image

4) The Select statement’s second argument contains our callback method.  The select statements retrieves the data using the SqlDataSource query we specified and returns a list of data in an ienumerable object.  Then it passes this data as an argument to our callback method which gets called inmediately.  As you can see, peeking into the myReturnedData parameter shows the results of my Select statement I specified in the SqlDataSource control.

image

5) Now we check again the IsBoundUsingDataSourceID property (which we know is set to true) and raise the OnDataBinding event:

image

6) Next, we call the PerformDataBinding method we overrode from the DatBoundControl class:

image

7) Here we step into the PerformDataBinding method (passing the resulting data in the input parameter).  First we call the base class’ PerformDataBinding method:

image

8) Next check if the data is null.  If there is data to be processed, we create new table, table row and table cell objects.  Then we check our control’s DataTextField parameter to see which column was specified in the client’s code.

image

9) If a column was specified in the control’s DataTextField, then we proceed to iterate over the data list.  We declare a PropertyDescriptorCollection object to store the row for each iteration.  Then we use the DataTextField property to grab the column’s value we are interested from the row.  After the iteration we add the table to the client’s web site control collection:

image 

10) In the end we have our resulting column values displayed in the browser:

image

No comments:

Post a Comment