Для решения вашей проблемы потребуются три разных повторителя, один из которых вложен в другой. Начните с разметки, как это.
<table>
<tr class="headerRow">
<td> </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>
Привязка к этому может быть немного хитрой. Идея состоит в том, чтобы сначала связать строку заголовка, а затем связать строки данных и столбцы. Вы захотите обработать привязку данных через код, используя событие OnItemDataBound, чтобы вы могли подключить вложенный повторитель к необходимым данным.
Сначала мы свяжем строку заголовка с Годами. Вам необходимо выделить коллекцию уникальных лет, присутствующих в вашем источнике данных, и сохранить ее в закрытой переменной. Вам потребуется доступ к нему во время привязки данных других ретрансляторов позже. Это будет служить источником данных для строки заголовка, создавая одну ячейку / столбец для каждого года.
List<DateTime> _Years = dataSource.SelectMany(x => x.data).GroupBy(y => y.Year);
rptYear.DataSource = _Years;
rptYear.DataBind();
Теперь вам нужно привязать повторитель имени к исходному источнику данных. Что-то вроде
rptName.DataSource = dataSource;
rptName.DataBind();
Это создаст одну строку для каждого элемента в вашем списке.
Во время события OnItemDataBound для этого повторителя вам нужно будет привязать вложенный повторитель к списку финансовых лет - по одному на каждый финансовый год в нашей переменной _Years - с любыми применимыми данными из элемента данных текущей строки. Это немного сложно, но я попытаюсь объяснить:
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();
}
Удачи.
Раньше мне приходилось делать это с несколькими вложенными повторителями для промежуточных и итоговых строк. Я начал видеть вложенные повторители во сне.