DataControls 101 Part 2: Why you should love Datasource controls

I've been scouring the forums recently trying to find problems people encounter when using the data controls. One thing that I found is mostly asked for but is kind of a hidden art:
How do I use the GridView/ListView/DetailsView.... without a Datasource control? (and still get all the fancy features offered).

Now I've always know that you can do this but I decided to explore how much is involved in making this stuff to work. This sample shows a GridView using raw Linq queries in C#. Let’s dive into the code:

Markup

<asp:GridView

    DataKeyNames="ProductID"

    ID="products" runat="server"

    AllowPaging="True"

    AllowSorting="True"

    CellPadding="4"

    ForeColor="#333333"

    GridLines="None">

    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />

    <Columns>

        <asp:CommandField

            ShowDeleteButton="True"

            ShowEditButton="True"

            ShowSelectButton="True" />

    </Columns>

    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />

    <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />

    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

    <EditRowStyle BackColor="#999999" />

    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />

</asp:GridView>

Code behind

protected void Page_Load(object sender, EventArgs e) {

    // Setup Events

    products.RowEditing += new GridViewEditEventHandler(OnProductsRowEditing);

    products.RowUpdating += new GridViewUpdateEventHandler(OnProductsRowUpdating);

    products.RowCancelingEdit += new GridViewCancelEditEventHandler(OnProductsRowCancelingEdit);

    products.RowDeleting += new GridViewDeleteEventHandler(OnProductsRowDeleting);

    products.Sorting += new GridViewSortEventHandler(OnProductsSorting);

    products.PageIndexChanging += new GridViewPageEventHandler(OnProductsPageIndexChanging);

 

    if (!IsPostBack) {

        DataBindProducts();

    }

}

 

private void DataBindProducts() {

    using (NorthwindDataContext db = new NorthwindDataContext()) {

        products.DataSource = db.Products;

        products.DataBind();

    }

}

The code above can be done in the markup but I chose to do it in the code behind. The code in Page_Load is very basic and self explanatory.

Editing

private void OnProductsRowEditing(object sender, GridViewEditEventArgs e) {

    // Set EditIndex

    products.EditIndex = e.NewEditIndex;

    DataBindProducts();

}

The Edit event handler is pretty simple and straight forward. We set the new edit index and Databind the grid.

Paging

private void OnProductsPageIndexChanging(object sender, GridViewPageEventArgs e) {

    products.EditIndex = -1;

    products.PageIndex = e.NewPageIndex;

 

   DataBindProducts();

}

Paging code is similar to editing; we just set the new page index and reset the edit index to -1.

Cancel

private void OnProductsRowCancelingEdit(object sender, GridViewCancelEditEventArgs e) {

    // Reset the edit index

    products.EditIndex = -1;

    DataBindProducts();

}

Now the hard stuff...

Sorting

private void OnProductsSorting(object sender, GridViewSortEventArgs e) {

    products.EditIndex = -1;

    products.PageIndex = 0;

 

    if ((SortExpression == e.SortExpression) && (SortDirection == SortDirection.Ascending)) {

        e.SortDirection = SortDirection.Descending;

    }

 

    using (NorthwindDataContext db = new NorthwindDataContext()) {

        products.DataSource = QueryableExtensions.SortBy(db.Products, e.SortExpression, e.SortDirection);

        products.DataBind();

    }

 

    SortExpression = e.SortExpression;

    SortDirection = e.SortDirection;

}

Woah! What’s going on here? When you examine the event arguments of the sorting event you will realize that the SortExpression changes but the SortDirection is always Ascending... This seems very weird, because the GridView keeps track of the sorting state (SortExpression and SortDirection). The problem is this state is only updated when the GridView is bound to a Datasource control :(. Why did we make that decision I’m not sure, but it is what it is so we must keep track of this ourselves.

public string SortExpression {

    get {

        return (string)ViewState["SortExpression"] ?? String.Empty;

    }

    set {

        ViewState["SortExpression"] = value;

    }

}

 

public SortDirection SortDirection {

    get {

        object o = ViewState["SortDirection"];

        return o != null ? (SortDirection)o : SortDirection.Ascending;

    }

    set {

        ViewState["SortDirection"] = value;

    }

}

We store the current SortExpression and SortDirection in the viewstate (GridView keeps it in control state). Then we do the obvious logic to change the sort direction appropriately.
The rest of the sort method just uses my helper to convert SortExpression and SortDirection to a Linq expression.

Updating/Deleting

private void OnProductsRowUpdating(object sender, GridViewUpdateEventArgs e) {

    GridViewRow row = products.Rows[e.RowIndex];

 

    IOrderedDictionary values = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

    DictionaryHelper.ExtractRowValues(products, values, row, true /* includeReadOnlyFields */, true /* includePrimaryKey */);

 

    // Update the product

    QueryableExtensions.UpdateProduct(values);

 

    products.EditIndex = -1;

    DataBindProducts();

}

 

private void OnProductsRowDeleting(object sender, GridViewDeleteEventArgs e) {

    GridViewRow row = products.Rows[e.RowIndex];

 

    IOrderedDictionary values = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

    DictionaryHelper.ExtractRowValues(products, values, row, true /* includeReadOnlyFields */, true /* includePrimaryKey */);

 

    // Delete the product

    QueryableExtensions.DeleteProduct(values);

 

    products.EditIndex = -1;

    DataBindProducts();

}

These 2 methods are pretty similar in nature.First thing you may realize when examining the EventArgs in these methods are that all the dictionaries are empty! (again, another weird design choice) There is some logic in the Databound controls that detects if it is bound to a Datasource control or Datasource. In the case of the GridView, it calls ExtractRowValues to fill the dictionaries for update/delete if it is bound to a Datasource control. Since this function is protected the only other way to get the values from the row is to use find control and manually extract the data yourself, which can be a mess. I used private reflection in my sample to call the function and fill the dictionary (yes it's a little hacky :)).

Other random other pieces of code:

The calls to QueryableDataSourceHelper.SortBy, QueryableDataSourceHelper.UpdateProduct, QueryableDataSourceHelper.DeleteProduct, can be seen as specific implementations of a Datasource control’s ExecuteDelete, ExecuteInsert, and ExecuteSelect (sorting handled here) methods.

What your Datasource does for you:

  • CRUD operations for a specific technology (SQL/XML/objects)
  • Sorting and paging
  • Conversion of values from controls/querystring/session…etc through parameters.

If you look at my previous post and the amount of code I wrote to get it working compared to the above is mind boggling.

DON'T TAKE YOUR DATASOURCE FOR GRANTED.

Here is a link to the sample solution.

4 Comments

  • i'm trying your code about datasource controls, but i have problem with "using (NorthwindDataContext db = new NorthwindDataContext())". i don't know NorthwindDataContext db are from. help please. thanks,

    best regard,
    reys

  • Really nice tutorial, but it seems like the sorting is forgotten as soon as you click on another page - using paging.

  • @Dofs you're right. You need to use the current sort expression and direction in DataBindProducts. Should be a trivial fix though! An exercise left to the reader :)

  • I was not able to find the link to the source on your sky drive. It appears to possibly have been pushed off based ont eh publish date of the article. There is nothing listed prior to Dec 2008, and I don't see an accurate name reflected in the downloads matching the article title for example.

    Thanks.

Comments have been disabled for this content.