0

I have a repeater wanting to display Categories and products. Category should appear once and products would appear the number of products i have. Below is my markup

<asp:Repeater ID="rpt1" runat="server" OnItemDataBound="rpt1_ItemDataBound">
    <ItemTemplate>
        <asp:Label ID="lblCategory" runat="server"></asp:Label>
        <asp:Label ID="lblProductName" runat="server"></asp:Label>
    </ItemTemplate>
</asp:Repeater>

I bind the products at page load

if (!Page.IsPostBack)
{
    LoadData();
}

My codebehind

protected void rpt1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        Product p = (Product)e.Item.DataItem;

        Label lblCategory = e.Item.FindControl("lblCategory") as Label;
        Label lblProductName = e.Item.FindControl("lblProductName") as Label;

        lblProductName.Text = p.ProductName;
        lblCategory.Text = p.Category.CategoryName;
    }
}

Everything works but my category text is repeated more than once (its shown the number of products i have). How could i display the category just once?

Edit

Cat 20 is Stationery
Cat 30 is Computer Items
Cat 40 is Toiletry

**Id CatId ProductName**
1   20     Pencil
1   20     Pen
1   30     Compact Disc
1   30     USB
1   30     Hard drive
1   40     Toothpaste
1   40     Toothbrush

I get my data in an

Iqueryable<Product> LoadData = myContext.GetProducts();
Computer
  • 2,149
  • 7
  • 34
  • 71
  • I think using nested repeater would be better in that case. I mean first repeater for Category and the nested repeater for products of it's category. – Selim Yildiz Apr 01 '20 at 07:13
  • So category repeater and product repeater? Product repeater is inside the ItemTemplate of the category repeater? What about the data for each repeater? Appreciate any examples. – Computer Apr 01 '20 at 07:20
  • I have added an answer, please check it. I tried to explain two ways to do this. – Selim Yildiz Apr 01 '20 at 07:33

2 Answers2

2

I think using nested repeater would be better in that case. I mean first repeater for Categories and the nested repeater for Products of it's category. See: Nested repeater.

<asp:Repeater runat="server" id="Categories">
  <ItemTemplate>
    Category: <%# Eval("CategoryName") %>
    Products:
    <asp:Repeater runat="server" DataSource='<%# Eval("Products") %>'>
      <ItemTemplate><%# Eval("ProductName") %></ItemTemplate>
    </asp:Repeater>
  </ItemTemplate>
</asp:Repeater>

But to do this you need to work on data model a bit, your model should be like this:

public class Category 
{
    public string CategoryName {get; set;}
    public IEnumerable<Product> Products {get; set;}
}

public class Product 
{
    public string ProductName {get; set;}
}

Or dirty-quick solution is that by comparing category name to previous row for each repeater item. If category name is changed then display new category name, otherwise set empty:

protected void rpt1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        Product p = (Product)e.Item.DataItem;

        Label lblCategory = e.Item.FindControl("lblCategory") as Label;
        Label lblProductName = e.Item.FindControl("lblProductName") as Label;

        lblProductName.Text = p.ProductName;

        if(e.Item.ItemIndex > 0)
        {
            RepeaterItem previousRepeaterItem = rpt1.Items[e.Item.ItemIndex - 1];
            var previousCategoryName = (previousRepeaterItem.FindControl("lblCategory") as Label).Text;
            if(previousCategoryName != p.Category.CategoryName)
            {
                lblCategory.Text = p.Category.CategoryName; 
            }
        }
    }
}
Selim Yildiz
  • 5,254
  • 6
  • 18
  • 28
  • I'll have a play about as ATM im having duplicate data for the categories. Should i need two datasources? One for the categories and one for products? At the moment i have all the products coming through with same categories. Thanks – Computer Apr 01 '20 at 07:44
  • The dirty trick is too dirty to use. I got a clean dirty approach to go with, You might want to see and give feedback about. Moreover, you would want to correct the model that specified Employee for products as IEnumerable. – Jamshaid K. Apr 01 '20 at 07:50
  • @Computer, as long as you have data model same as I show in answer then you don't need two seperate datasources. Just fetch categories with products once from db and bind it. – Selim Yildiz Apr 01 '20 at 07:52
  • I have edited my original post to display the way the data is and how i get it. Im running the code but getting a few errors. Thanks – Computer Apr 01 '20 at 08:18
0

I am not sure about the complexity of the product data that you wish to display. Yet I would like to suggest you using the nested Repeaters, one repeater will have category name and the second would have the products. On Page_Load, you should fetch all distinct categories and bind the first repeater And on ItemDataBound of first repeater you must fetch products relevant to the category and bind the nested repeater.

e.g:

<asp:Repeater ID="rpt1" runat="server" OnItemDataBound="rpt1_ItemDataBound">
    <ItemTemplate>
        <asp:Label ID="lblCategory" runat="server" Text='<%# Eval("Category") %>'></asp:Label>
        <asp:Repeater ID="rpt2" runat="server">
            <ItemTemplate>
                <asp:Label ID="lblProductName" runat="server" Text='<%# Eval("ProductName")%>'></asp:Label>
            </ItemTemplate>
        </asp:Repeater>
    </ItemTemplate>
</asp:Repeater>

Page Load:

protected void Page_Load(object s, EventArgs e)
{
    // Fetch All the Categories, Distinct Elements
    DataTable dt = new DataTable();
    dt.Columns.Add("Category");
    dt.Rows.Add("Sanatizers");
    dt.Rows.Add("Soaps");
    rpt1.DataSource = dt;
    rpt1.DataBind();
}

Item Data Bound:

protected void rpt1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        Label lblCategory = (Label)e.Item.FindControl("lblCategory");

        Repeater rpt2 = (Repeater)e.Item.FindControl("rpt2");
        if (lblCategory != null)
        {
            // Fetch products by category
            var dt = new DataTable();
            dt.Columns.Add("ProductName");
            dt.Rows.Add("Dettol");
            dt.Rows.Add("Safeguard");
            dt.Rows.Add("Fair and Lovely");
            rpt2.DataSource = dt;
            rpt2.DataBind();
        }
    }
}

PS: The code is not tested, it might need some love.

Jamshaid K.
  • 3,555
  • 1
  • 27
  • 42