views:

1240

answers:

6

Hi, 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.

A: 

You aren't repeating Colour and Number, so it isn't really a header.

ck
A: 

Ah - you're after a grouping repeater. The solution that I've adapted in the past seems to have disappeared. Maybe you can find it. But anyway, a search for grouping repeaters should help.

darasd
Cheers, that's what i'm looking for.
ctrlalt3nd
That article no longer works.
Chris Lively
+2  A: 

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;
    }
}
Joel Coehoorn
A: 

To joel Solution, I may advice following - change this line:

header.Text = string.Format("<strong> {0} </strong><br/><table>", CurCountry);

to

header.Text = string.Format(header.Text, CurCountry);

And than you can customize look & feel from .as?x file.

Sergey Osypchuk
A: 

There seem to be a few bugs in the dotnetjunkies code but I've found a much simpler solution here http://www.west-wind.com/weblog/posts/140753.aspx

Now repeaters don't support grouping, so you have to do this on your own, but it's pretty easy to do. In the repeater above the following data expression is responsible for the grouping:

<%# this.RenderGroup(Eval("Group") as
string) %>

The expression calls a method on the form to render an extra div tag. The code for this simple method looks like this:

string LastGroup = "@#@~";
protected string RenderGroup(string Group) {   
    if (Group == this.LastGroup)       
    return "";     // *** Group has changed    
    this.LastGroup = Group;    
    return "<div class='groupheader'>" +Group + "</div>";
}

This code gets called for every item and it simply checks to see if the group has changed. If it hasn't nothing is returned otherwise a simple div tag is returned as a string to embed into the markup. I chose to send back the HTML but I suppose you could also return true or false and conditionally render a control in the Repeater which would make the CSS Nazis happier .

ctrlalt3nd
A: 

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.

Ray Booysen