Archives

Archives / 2008 / October
  • 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...

  • Data Controls 101

    I’ve worked with asp.net a bit at school before I came to Microsoft. Being the “data dev” as you would imagine, I get lots of bugs that have to do with the data (datasource/databound) controls. I also scan the internal and external forums and try to help customers find solutions to their problems. This blog post is the first of a series on common scenarios using data controls, that I call Data Controls 101.

    When new developers come to asp.net from other web technologies/backgrounds the tendency is to continue doing things as they did before, when in fact they should learn the paradigms we expect in asp.net webforms (mvc is kind of different).

    These posts will be scenario driven with tidbits of information here and there. Let’s start with a simplest case:

    I want to show a list of categories from my database.

    Before you pick a control and rush to go write code, you should ask other questions that will help narrow the choices down. Do I want to delete, update, insert, select? Do I want to define my control template manually or use column auto generation? After you decide what set of functionality you want there is still probably a bunch combinations of controls you want choose to accomplish your goal.

    Controls

    Select

    Delete

    Inline Edit

    Insert

    Paging

    Sorting

    Column Autogeneration

    GridView

    x

    x

    x

    -

    x

    x

    x

    ListView

    x

    x

    x

    x

    x (DataPager)

    x

    -

    Repeater

    -

    -

    -

    -

    -

    -

    -

    DetailsView

    -

    x

    x

    x

    x

    -

    x

    FormView

    -

    x

    x

    x

    x

    -

    -

    DataList

    x

    x

    x

    -

    -

    -

    -

    The above table is a summary of what those controls on the left support out of the box without much added effort (some other controls can do the same thing with a little more work).

    Ok so we’ve decided to we want a tabular layout, paging, sorting and selection with inline editing. Looking at the table I’d pick the GridView or ListView.

    Next goal is to choose the datasource control. The same kinds of questions apply when picking the datasource control, but it is more specific to how you want to access your data. In my case I’ll use LinqDataSource.

    Finally the code:

    <asp:LinqDataSource ID="categoriesSource"

        EnableUpdate="true"

        runat="server"

        ContextTypeName="FowlerSample.NorthwindDataContext"

        TableName="Categories">

    </asp:LinqDataSource>       

     

    <asp:GridView ID="categories"

        runat="server"

        AllowPaging="True"

        AllowSorting="True"

        DataKeyNames="CategoryID"

        DataSourceID="categoriesSource">

        <Columns>

            <asp:CommandField ShowSelectButton="True" ShowEditButton="true" />

        </Columns>

    </asp:GridView>

    That’s all you have to write and suddenly things just work. Next time I'll go over some of how the “magic” works.

    Read more...

  • First Post

    Hey everyone, my name is David Fowler and I'm new to the ASP.NET team. I've been an intern here in Redmond for the last 2 summers and I’m glad to be back as a FTE (Full Time Employee in microspeak). My responsibilities include all things data related to ASP.NET. This includes things like the Datasource and Databound controls as well as Dynamic Data. I look forward to posting some interesting stuff in the near future!

    Read more...