Gridviews zijn makkelijke controls om snel en overzichtelijk data te tonen op je ASP.NET pagina. Standaard ziet het er niet echt aantrekkelijk uit, maar door alle styling mogelijkheden kun je er redelijk gemakkelijk wat moois van maken. Maar hoe laat je geneste data zien; een master-detail relatie binnen je gridview? Nou, ook dat is mogelijk!

Gridviews zijn makkelijke controls om snel en overzichtelijk data te tonen op je ASP.NET pagina. Standaard ziet het er niet echt aantrekkelijk uit, maar door alle styling mogelijkheden kun je er redelijk gemakkelijk wat moois van maken. Maar hoe laat je geneste data zien; een master-detail relatie binnen je gridview? Nou, ook dat is mogelijk!
Ik ga in dit voorbeeld project een gridview maken met orders, met daarin een gridview van de bestelde producten in de verschillende orders. Door middel van Ajax kunnen we het uiteindelijk ook nog mooi dynamisch maken.
Parent gridview
In dit voorbeeld ga ik uit van de orders die in de “Adventureworks” voorbeeld database van SQL Server 2005 staan.
We beginnen met de gridview die de orders toont. Dit is dus de “parent” gridview, waar we later een “child” gridview aan gaan toevoegen. De gridview plaats ik op de pagina en gebruik een SqlDataSource om de orders op te halen (in dit geval van iedereen die “Sandberg” als achternaam heeft, zodat ik een mooie hoeveelheid records terug krijg). In de “Orders” kolom wil ik graag niet alleen het aantal orders zien, maar ook de gekochte producten, daarom moet ik deze kolom gaan aanpassen. Dat doe ik via “Edit Columns”.
Nested GridView met ASP.NET
In het dialoogvenster kies ik de “Orders” kolom en converteer deze naar een TemplateField, wat wil zeggen dat ik niet zomaar de data toon, maar dat ik een template aan ga maken van hoe deze kolom eruit moet komen te zien.
Nested GridView met ASP.NET
In de ASP zien we de volgende code verschijnen. Er is dus een <ItemTemplate> bijgekomen.
<asp:TemplateField HeaderText="Orders" SortExpression="Orders">
  <ItemTemplate>
   <asp:Label ID="Label1" runat="server" Text='<%# Bind("Orders") %>'></asp:Label>
 </ItemTemplate>
</asp:TemplateField>
In deze template kunnen we van alles doen. Dropdownlists, textboxes, buttons, verzin het maar. Ik ga er een andere Gridview in plaatsen. De “Child” gridview dus.
Child GridView
En dan nu de grote truc! Hoe bind ik de data aan deze child gridview? Ik kan aan de “DataSource” property een methode opgeven die de data aanlevert aan de child gridview. Aan deze methode wil ik, in dit geval, dan mee geven van wie ik de OrderDetails ik wil zien. Hieronder zie je hoe ik de child-gridview in de itemtemplate heb geplaatst en de datasource property gevuld heb met de “GetOrderRows” methode (die we zo gaan schrijven). Aan deze methode geef ik het “contactID” mee.
<asp:TemplateField HeaderText="Orders" SortExpression="Orders">
  <ItemTemplate>
    <asp:Label ID="Label1" runat="server" Text='<%# Bind("Orders") %>'></asp:Label>
     <asp:GridView id="gvOrders" DataSource='<%# GetOrderRows(Convert.ToInt32(Eval("contactid"))) %>'  runat="server" >
Nu is het een kwestie van het schrijven van de GetOrderRows methode. Dit kan op diverse manieren, maar ik heb ervoor gekozen om in de Page_load een DataTable te vullen met alle orderdetails van de customers uit de parent gridview. En in de GetOrderRows methode maak ik dan een DataView aan waar in ik een RowFilter specificeer voor de orderdetails die behoren tot het juiste ContactID. Dit ContactID krijg ik dus binnen als parameter van de GetOrderRows methode.
public partial class _Default : System.Web.UI.Page
{
    DataTable ordertable;
    protected void Page_Load(object sender, EventArgs e)
    {
    ordertable = new DataTable();
    SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["AdventureWorksConnectionString"].ConnectionString);
    string query = "Select c.contactID as ContactID, p.Name as Productname, d.orderqty as Quantity ,LineTotal " +
      "from sales.salesOrderDetail as d " +
      "inner join sales.salesOrderHeader as s on d.salesorderid = s.salesorderid "+
      "inner join person.contact as c on s.contactID = c.contactID "+
      "inner join production.product as p on p.productid = d.productid "+
      "where c.Lastname = 'sandberg'";
    SqlDataAdapter da = new SqlDataAdapter(query, conn);
    da.Fill(ordertable);
    }
    protected DataView GetOrderRows(int id)
    {
    DataView orderView = new DataView(ordertable);
    orderView.RowFilter = "ContactID = " + id.ToString();
    return orderView;
    }
}
Beetje Styling er overheen en dan komt het er zo uit te zien!
Nested GridView met ASP.NET
AJAX
Nu werkt dit prima als je niet al te veel data hebt in je Child GridView. Maar misschien heb je wel een hele hoop data en wil je deze alleen laten zien als je het nodig hebt, bijvoorbeeld door op een “+” te klikken en dat het dan uitklapt. Hmmm, klinkt als een Ajax-uitdaging.
Ten eerste voeg ik een button toe aan de ItemTemplate die voor het in-en uitklappen moet zorgen en tevens de data binding verzorgt op het moment dat we deze willen tonen. De “DataSourceId” property van de Child gridview kan er dus uit.
<ItemTemplate>
  <asp:Label ID="Label1" runat="server" Text='<%# Bind("Orders") %>'></asp:Label>
  <asp:Button ID="btnShow" runat="server" CommandArgument='<%# (Convert.ToInt32(Eval("contactid")))%>' onclick="ShowGridView" Text="+" class="togglebutton"/>
  <asp:GridView Visible="false" id="gvOrders" runat="server">
Door middel van de CommandArgument geef ik mee welke Gridview ik wil tonen of verbergen. De knop roept de “ShowGridView” methode aan die er als volgt uit is komen te zien:
protected void ShowGridView(object sender, EventArgs e)
    {
    Button btn = (Button)sender;
    if (btn.Text == "+")
    {
        btn.Text = "-";
        GridView gv = (GridView)btn.Parent.FindControl("gvOrders"); //vind de child GridView
        gv.DataSource = GetOrderRows(Convert.ToInt32(btn.CommandArgument));
        gv.DataBind();
        gv.Visible = true;
    }
    else
    {
        btn.Text = "+";
        GridView gv = (GridView)btn.Parent.FindControl("gvOrders");
        gv.Visible = false;   
    }
    }
Het interessantste stukje is het zoeken van de Child GridView. Eerst zoek ik de Parent van de Button, dit is de rij van de Parent GridView waarin de button staat. Vervolgens zoek ik in deze rij naar de Child GridView “gvOrders”. Nu kan ik de Datasource zetten naar de “GetOrderRows” die we voorheen al gebruikten en geef ik het CommandArgument van de button mee waarin de “contactId” staat.
ScriptManager en Updatepannel op de pagina plaatsen en Off-you-go!
Nested GridView met ASP.NET
SourceCode en verbeteringen
De SourceCode kun je hier downloaden. In het project staan beide oplossingen (met en zonder Ajax). Hopelijk kun je hier zelf mooie dingen mee maken. Ik sluit niet uit dat er hier en daar nog wel wat te verbeteren is, Error-afhandeling toe te voegen is of andere aanpassingen gewenst zijn. Ik hou me dan ook warm aanbevolen voor al jullie suggesties!
En als je ‘m ergens toepast.. laat het even weten, altijd leuk!