Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asp:repeater - headers at section change

is there any way to bring up a subheader row at changes in a field in an databound asp:repeater control e.g.

Instead of

country | colour | number
uk | red | 3
uk | green | 3
france | red 3

Do this:

==UK==
colour | number
red | 3
green 3
==FRANCE==
colour | number
red | 3

Many thanks for any help.

like image 409
ctrlalt3nd Avatar asked Mar 02 '23 01:03

ctrlalt3nd


2 Answers

There's no built-in support, but that doesn't mean it's impossible.

You'll need to over-ride the OnItemDataBound event, and have something like this in markup:

<asp:Repeater OnItemDataBound="NextItem" ... >
    <ItemTemplate><asp:Literal Id="Header" Visible="False" Text="{0}<strong>{1}</strong><br/><table>" ... />
         <tr>
             <td><asp:Label id="Color" Text="<%# Eval("Color")" ... /></td>
             <td><asp:Label id="Number" Text="<%# Eval("Number")" ... /></td>
         </tr>
    </ItemTemplate>
</asp:Repeater></table>

Then in the code-behind:

private string CurCountry = string.Empty;

private void NextItem(object sender, RepeaterItemEventARgs e)
{
    if ( e.Item.ItemType != ListItemType.Item 
      && e.Item.ItemType != ListItemType.AlternatingItem) return;

    DbDataRecord row = (DbDataRecord)e.Item.DataItem;

    if (CurCountry != row["country"].ToString() )
    {
        string prev = (CurCounter == string.Empty)?"":"</table>";
        CurCountry = row["country"].ToString();

        Literal header = (Literal)e.Item.FindControl("Header");
        Literal footer = (Literal)e.Item.FindControl("Footer");

        header.Text = string.Format(header.Text, prev, CurCountry);
        header.Visible = true;
    }
}
like image 143
Joel Coehoorn Avatar answered Mar 05 '23 16:03

Joel Coehoorn


Another solution is to simply use two repeaters, one nested within the other. You can pass your groups with the child records to the first repeater, and on the ItemDataBound of the groups repeater, pass the child records to the child repeater and call DataBind() there.

This is more code but does actually give you more control over the layout without having HTML creation code in your code-behind.

As you can see here, we have a parent repeater and in the item template we can customise each group as we see fit. In the ChildRepeater, we have our item template in which we can customise each item inside the grouping. Very clean and all with declarative UI.

<asp:Repeater runat="server" id="GroupRepeater">
 <ItemTemplate>
   <asp:Literal runat="server" id="HeaderText" />
   <asp:Repeater runat="server id="ChildRepeater">
    <ItemTemplate>
      <asp:Literal runat="server" id="InfoGoesHere" />
    </ItemTemplate>
   </asp:Repeater>
 </ItemTemplate>
</asp:Repeater>

In the code behind we can have something like this:

private void GroupRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
  //Get the child records, this can be any data structure you want
  SomeChildCollection children = ((SomeGroupCollection)e.Item.DataItem).Children;
  //Find the child repeater
  Repeater childRepeater = e.Item.FindControl("ChildRepeater") as Repeater;
  childRepeater.ItemDataBound += SomeMethod;
  childRepeater.DataSource = children;
  childRepeater.DataBind();
}

After binding each child you can subscribe to the ItemDataBound event and do the child binding to controls as you see fit.

like image 29
Ray Booysen Avatar answered Mar 05 '23 16:03

Ray Booysen