Contents tagged with DataSourceControls

  • 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.

    Read more...