posted on Wednesday, February 02, 2005 9:41 AM by mlorengo

Handling Complex Objects in the GridView Control

The new GridView in Asp.Net 2.0 makes displaying business objects just as easy as displaying DataSets. However when it comes to displaying complex business objects (my definition) it isn't that apparent (at least to me) how to go about doing this.

When I say complex business object, I mean a business object that is composed of more than just properties of simple types (int, string, etc), but instead has properties consisting of other objects or even collections of objects. A prime example of this in my case is the Wine class.

In the class diagram above, you can see that the Wine has a Varietals property that is a collection of WineVarietal classes, in turn, the WineVarietal class is composed of an Id, Percent, and a Varietal class. We're talking complex, not rocket science, but complex. The Class diagram illustrates how I chose to model the relationship of a Wine to it's Varietal. In this case a Wine can be composed of one or more varietals of grape in varying percentages. Please see my Varietal is the Spice of Life for more details on the modeling.

In the declaration of the .aspx page GridView control, I have the following fields bound. Notice the Varietals field, it's the same as all of the others.

<asp:gridview id="wineGridView" runat="server" AutoGenerateColumns="False">
  <Columns>
    <asp:BoundField HeaderText="Id" DataField="Id" SortExpression="Id"></asp:BoundField>
    <asp:BoundField HeaderText="Vintage" DataField="Vintage" SortExpression="Vintage"></asp:BoundField>
    <asp:BoundField HeaderText="Name" DataField="Name" SortExpression="Name"></asp:BoundField>
    <asp:BoundField HeaderText="Appellation" DataField="Appellation" SortExpression="Appellation"></asp:BoundField>
    <asp:BoundField HeaderText="Varietals" DataField="Varietals" SortExpression="Varietals"></asp:BoundField>
    </Columns>
</asp:gridview>

However the GridView will display the following. See how the Varietals column simply outputs the class name of the field. What I would really like to do is have it display the list of varietal components, along with there percentage makeup.

IdVintageNameAppellationVarietals
8220002000 Ravenswood Winery Pickberry Vineyards Sonoma MountainSonoma MountainLorengo.VirtualCellar.Business.WineVarietalCollection

It turns out that the GridView supports the concept of a <asp:TemplateField> which allows for the customization of the output. What I will need to do is replace the <asp:BoundField> for the Varietals property, and instead insert a <asp:TemplateField>. Inside of the <asp:TemplateField> I will add a DataList control, and give it an id="varietalDataList". For the <ItemTemplate> in the DataList I will use the Container.DataItem property, which should point to a WineVarietal item in my Varietals collection. I can then cast the DataItem to the appropriate type.

For example ((WineVarietal)Container.DataItem).Percent will give me the percentage of the grape varieatal in the current wine. See the code below.

<asp:gridview id="wineGridView" runat="server" AutoGenerateColumns="False" OnRowDataBound="WineGridView_RowDataBound">
  <Columns>
    <asp:BoundField HeaderText="Id" DataField="Id" SortExpression="Id"></asp:BoundField>
    <asp:BoundField HeaderText="Vintage" DataField="Vintage" SortExpression="Vintage"></asp:BoundField>
    <asp:BoundField HeaderText="Name" DataField="Name" SortExpression="Name"></asp:BoundField>

    <asp:TemplateField HeaderText="Varietals" SortExpression="Varietals" >
      <ItemTemplate>

        <asp:DataList id="varietalDataList" runat="server">
          <ItemTemplate>
            <%# ((Varietal)((WineVarietal)Container.DataItem).Varietal).Name %>
            (<%# ((WineVarietal)Container.DataItem).Percent %>%)
          </ItemTemplate>
        </asp:DataList>

      </ItemTemplate>
    </asp:TemplateField>

    <asp:BoundField DataField="Appellation" SortExpression="Appellation"></asp:BoundField>
    <asp:BoundField HeaderText="ItemState" DataField="ItemState" SortExpression="ItemState"></asp:BoundField>
  </Columns>
</asp:gridview>

Ahh, but how does it know to bind the items in the DataList to the WineVarietal object of the current wine? You may have noticed the added attribute in the <asp:GridView> control. To be more specific, OnRowDataBound="WineGridView_RowDataBound".

This let's us add some special code to handle the wiring up of the Wine's Varietals collection in the pages codebehind.

protected void WineGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
  Wine w = null;

  GridViewRow r = e.Row;
  if (r.DataItem != null) // Make sure we're not in the Header or Footer row
  {
    w = r.DataItem as Wine;
  }

  DataList varietalDataList = e.Row.FindControl("varietalDataList") as DataList;
  if (varietalDataList != null && w != null)
  {
    varietalDataList.DataSource = w.Varietals;
    varietalDataList.DataBind();
  }

For each row in the GridView (including the Header and Footer rows), we will find "varietalDataList" control and update it's DataSource to the Varietals collection of the current Wine object. Then we call it's DataBind() method and woo hoo! Here's what we get!

 

IdVintageNameVarietals ItemState
8220002000 Ravenswood Winery Pickberry Vineyards Sonoma Mountain
Merlot (53%)
Cabernet Sauvignon (45%)
Cabernet Franc (2%)
Sonoma MountainClean

I like it. Now, I'm not sure if this is the best way to do it. It just works for me. If anyone has any other suggestions as to accomplish this, I would sure be interested in hearing about it. My next step is to code up a general Producer and Wine search page, so I'll take what I've learned today and apply it to those pages.

 

Comments