views:

366

answers:

2

Hi All,

I have an objectdatasource which i want to bind to a repeater. the problem is, I cannot work out how to display a variable amount of columns with a variable amount of rows.

for example:

The dataset I have is structured like this. The objectdatasource is a List<item>.

item {
  string name;
  List<itemdata> data;
}

itemdata {
  DateTime year;
  double amount;
}

so basically i want to make a table

      |  year  |  year  |  year  |  year
 name | amount | amount | amount | amount
 name | amount | amount | amount | amount
 name | amount | amount | amount | amount
 name | amount | amount | amount | amount

The number of items are variable, as well as the number of itemdata that the item contains.

Hope someone can point me in the right direction.

Thanks

A: 

I suggest you convert your data into a new data structure like this:

name_data {  
  string name;
  int[] amounts;
}

You would then bind your repeater to a List(name_data>.

To create this, first, iterate through item and data lists and keep a list (a List probably) of all the unique years you need to report on. Sort the resulting list so that the years are in order. Now, the indexes of the year list correspond to the column numbers in your output table. Next, iterate through the item list again, this time creating a new name_data object for each item object. The name_data constructor would look like this:

public name_data(string name, int yearCount) {
  this.name = name;
  amounts = new int[yearCount];
}

The yearCount is the number of items in the year list.

Finally, step through the data list for the current item, look up the year in the year list to get the index, then stick the amount value in the amount field in the corresponding amounts slot. Add your completed name_data to the List.

Once you are done, you should be able to bind your name_data list to your repeater

Ray
I'm not sure that structure would work for him. In his structure, he can assign an amount for a year in the middle where yours would assume that all items had data in the appropriate order.
Joel Etherton
Yes, in your answer, it requires that everything is in order and sorted. In my case there are times that it is not in order.
Mike
This is why I suggested a first pass through the data to grab the years and sort them. The a second pass to assign the data to the proper column.
Ray
+1  A: 

The solution to your problem will require three different repeaters, one of which is nested inside another. Begin with the markup like this.

  <table>
       <tr class="headerRow">
          <td> &nbsp;</td>
          <asp:Repeater ID="rptYearHeader" runat="server" OnItemDataBound="rptYearHeader_ItemDataBound">
              <ItemTemplate>
                 <td class="header"><asp:Literal ID="litYear" runat="server"></asp:Literal></td>
              </ItemTemplate>
          </asp:Repeater>
       </tr>
       <asp:Repeater ID="rptName" runat="server" ItemDataBound="rptName_ItemDataBound">
          <ItemTemplate>
             <tr>
                <td><asp:Literal ID="litName" runat="server"></asp:Literal></td>
                <asp:Repeater ID="rptAmounts" runat="server" OnItemDataBound="rptAmounts_ItemDataBound">
              <ItemTemplate>
                 <td><asp:Literal ID="litAmount" runat="server"></asp:Literal></td>
              </ItemTemplate>
          </asp:Repeater>
             </tr>
          </ItemTemplate>
       </asp:Repeater>
    </table>

Binding to this can be a little tricky. The idea is, first we bind the header row - then we bind down the data rows and across the columns. You will want to handle the data binding through code behind using the OnItemDataBound event so that you can wire up the nested repeater with the necessary data.

First we bind the header row with Years. You need to isolate a collection of unique years present in your datasource and keep it in a private variable. You will need to access it during the data binding of the other repeaters later. This will serve as the data source for the header row, creating one cell/column for each year.

List<DateTime> _Years =  dataSource.SelectMany(x => x.data).GroupBy(y => y.Year);
rptYear.DataSource = _Years;
rptYear.DataBind();

Now, you need to bind the Name repeater with your original data source. Something like

rptName.DataSource = dataSource;
rptName.DataBind();

This will create one row for each item in your list.

During the OnItemDataBound event for this repeater, you will need to bind the nested repeater to a list of fiscal years - one per each fiscal year in our _Years variable - with any applicable data from the current row's data item. This gets a little tricky, but I'll try to explain:

protected void rptName_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// get the data item being bound
item currentItem = e.Item.DataItem as item;

// bind the item's name to the literal
//...
//

// get a list of amounts to bind to the nested repeater
// because we cant be sure that every item has amount for all years
// we create a list that we know has all years and plug in the items 
// data accordingly.

    List<double> amounts = new List<double>();
     for (int i = 0; i < _Years.Count; i++)
     {
        // check whether the current item has data for the year
        dataItem di = currentItem.data.Where(d => d.Year == _Years[i]).FirstOrDefault();

        if(di == null)
        {
             // the year did not exist, so we add an amount of 0 
             amounts.Add(0);
        }
        else
        {
           // the year did exist, so we add that year's amount
           amounts.Add(di.amount);
        }
     }

     // we now have a list of amounts for all possible years, with 0 filling in
     // where the item did not have a value for that year

     // bind this to the nested repeater
     rptAmounts.DataSource = amounts;
     rptAmounts.DataBind();

}

Good luck.

I have had to pull this off with multiple nested repeaters for sub-total and grand total rows before. I started seeing nested repeaters in my sleep.

Ben Elder
This is brilliant response. It worked. Thank you very much. I think I'm also starting to see nested repeaters in my sleep. :P
Mike