3
votes

Wondering the best way to populate a GridView with my object data.

I have to show a product list from an complex object Sale, whose structure is something like this:

class Sale {
    int id;
    List<SaleItem> saleItems;
}

class SaleItem {
    int id;
    int quantity;
    Product product;

    BillingAddress billingAddress;
    ShippingAddress shippingAddress;
}

class Product {
    int id;
    string name;
    List<BuyingConfiguration> buyingConfigurations;
}

class BuyingConfiguration {
    string name; // like size, color, material
    string value;
}

and my grid should look like this:

Sale Items
+---------+---+------------+------------+----------------+
| Name    | # | B. Address | S. Address | Configurations |
+---------+---+------------+------------+----------------+
| Ferrari | 2 | --         | --         | Color: red     |
|         |   |            |            | Engine: Xyz    |
+---------+---+------------+------------+----------------+
| Jax     | 1 | --         | --         | Color: blue    |
|         |   |            |            | Engine: Abc    |
+---------+---+------------+------------+----------------+

Should I implement an ObjectDataSource for my Sale object? Is there any better solution?


EDIT 2: Let me try to make myself clear: the problem is not how to the display configurations. My problem is that the Sale object is returned to my code from the persistence layer, thats the why I dont want the GridView to access Database directly. Instead, it needs to load all its data from my Sale object, how to achieve that?


EDIT:

The Grid markup as requested:

<asp:GridView runat="server" ID="GridProdutos" OnRowDataBound="GridProdutos_OnRowDataBound"
    AutoGenerateColumns="False">
    <Columns>
        <asp:BoundField HeaderText="Name" />
        <asp:BoundField HeaderText="#" />
        <asp:BoundField HeaderText="B. Address" />
        <asp:BoundField HeaderText="S. Address" />
        <asp:BoundField HeaderText="Configurations" />
    </Columns>
</asp:GridView>

Ugly solution so far, using OnRowDataBound (I want to avoid that!):

protected void GridProdutos_OnRowDataBound(object sender, GridViewRowEventArgs e) {
    if (e.Row.DataItem == null)
        return;

    SaleItem item = (SaleItem )e.Row.DataItem;

    e.Row.Cells[0].Text = item.product.name;
    e.Row.Cells[1].Text = item.quantity.ToString();

    StringBuilder sbConfigurations = new StringBuilder();

    foreach (BuyingConfiguration configurationItem in item.product.buyingConfigurations) {
        sbConfigurations.AppendFormat("{0}: {1}<br />", configurationItem.name, configurationItem.value);
    }
    e.Row.Cells[4].Text = sbConfigurations .ToString();
}
2
please show your page markup with the grid definition. - Davide Piras
@DavidePiras it is very simple, added to the question - Yuri Ghensev
I guess you could add a repeater control for displaying the config items. The data source of the repeater should be bound by by using the "Bind()" method of the given data source, which eliminates the RowDataBound stuff. Hope this Helps!! - Praveen
@Praveen But first I got to know how to load all the grid data directly from my Sale object, not from database (as I pointed in my second edit). - Yuri Ghensev

2 Answers

3
votes

I would suggest using the TemplateColumns with binding expressions. You can bind your GridView to the saleItems list, and implement getter methods to render each field given a SaleItem instance. For example, your Name column could be defined as follows:

            <asp:TemplateField>
                <ItemTemplate>
                    <%# ((SaleItem)Container.DataItem).product.Name %>
                </ItemTemplate>
            </asp:TemplateField>

The same thing could be done with a custom getter method to move the access details into the code-behind:

            <asp:TemplateField>
                <ItemTemplate>
                    <%# getSaleItemProductName((SaleItem)Container.DataItem) %>
                </ItemTemplate>
            </asp:TemplateField>

Don't forget to add an Import directive to be able to reference your types:

<%@ Import Namespace="YouNamespaceHere" %>
1
votes
  dataGridView1.DataSource = ToDataTable(SaleItems);

you can bind data directly to dataGridView if you can convert complex object list to Datatable as bellow

 private DataTable ToDataTable(List<Sale> SaleItems)
    {
        DataTable returnTable = new DataTable("Sale");
        returnTable.Columns.Add(new DataColumn("Name"));
        returnTable.Columns.Add(new DataColumn("#"));
        returnTable.Columns.Add(new DataColumn("B. Address"));
        returnTable.Columns.Add(new DataColumn("S. Address"));
        returnTable.Columns.Add(new DataColumn("Configurations"));

        foreach (Sale item in SaleItems)
        {
            returnTable.AcceptChanges();
            DataRow row = returnTable.NewRow();
            row[0] = item.product.name;
            row[1] = item.quantity.ToString();
            row[2] = item.billingAddress.ToString();
            row[3] = item.billingAddress.ToString();
            StringBuilder sbConfigurations = new StringBuilder();

            foreach (BuyingConfiguration configurationItem in item.product.buyingConfigurations) {
               sbConfigurations.AppendFormat("{0}: {1}<br />", configurationItem.name, configurationItem.value);
            }
            row[4] = sbConfigurations.ToString();

            returnTable.Rows.Add(row);
        }
        return returnTable;
    }