Ваша проблема полностью в вашем Print
методе. Вы были укушены созданием слегка усложненного механизма отслеживания строк и сочетания его с магическими числами. Это заставляет вас размещать каждую таблицу после первой строки выше, чем она должна быть. Заголовок и промежуточные итоги не являются частью таблицы, поэтому у вас есть пара возможностей для ошибки. Как вы видели, таблицы не могут перекрываться, поэтому EPPlus начинает лаять на вас после того, как вы исчерпали свою свободу действий.
Все, что вам нужно сделать, это отслеживать текущую строку, в которую вы пишете, и учитывать пространство, занимаемое заголовком и нижним колонтитулом таблицы (промежуточными итогами), если вы их используете.
Вы заявляете следующее:
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
Но для записи в правильный ряд все, что вам нужно, это:
var rowNumber = 1;
Это правильно начнет запись ваших данных в первой строке листа Excel. Когда вы пишете строки таблицы, вы будете отслеживать и увеличивать только rowNumber
. Но как насчет верхнего и нижнего колонтитула каждой таблицы? Если вы начнете писать в первой строке таблицы, вы перезапишете верхний колонтитул, а если вы не учтете ни верхний, ни нижний колонтитулы, у вас начнутся коллизии, как вы видели. Итак, давайте сделаем это:
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
Это довольно очевидно, вы будете использовать rowAdders для переключения верхнего или нижнего колонтитула, когда это необходимо. rowNumber
всегда будет вашей текущей строкой для создания таблицы и записи ваших данных. Вы используете count
при определении таблицы, но мы сделали ее неактуальной для чего-либо еще, поэтому мы переместили ее:
var allocationList = allocationGroup.ToList();
//Moved here
var count = allocationList.Count();
Ваше заявление об использовании становится:
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
Далее, это не упоминается в вашем посте, но вы столкнетесь с проблемой следующего:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
Ваши имена таблиц должны быть уникальными, но вы вполне можете получить несколько распределений с одинаковым количеством, что заставит EPPlus выдать вам исключение для дублирования имени таблицы. Поэтому вы также хотите отслеживать индекс вашей текущей таблицы:
var rowNumber = 1;
var tableIndex = 0;
//...
foreach (var ag in allocation)
{
tableIndex += 1;
//...
}
И используйте его для обеспечения уникальных имен таблиц:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
Мы используем переменные управления форматом:
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Changes to
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
У вас есть небольшая опечатка здесь:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
//Should be:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,[Allocations])";
После того, как вы закончили определение таблицы, вы начинаете записывать свои данные с циклом foreach
. Чтобы предотвратить перезапись заголовка таблицы, если он существует, нам нужно продвинуться на одну строку. Мы также должны продвинуть одну строку для каждого FIRMWIDE_MANAGER_ALLOCATION
. Если вы используете промежуточные итоги, нам нужно продвинуться на одну строку после завершения цикла, чтобы правильно расположить следующую таблицу:
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
//...
rowNumber += 1;
}
rowNumber += rowAdderForFooter;
И это все. Теперь мы должным образом отслеживаем нашу позицию, используя только одну переменную, и при необходимости изменяем позицию, если на вашем столе есть верхний или нижний колонтитул.
Ниже приведен полный рабочий пример, который можно запустить в LinqPad, если вы добавите пакет EPPlus через Nuget. Он создает случайное количество групп распределений, каждое из которых имеет случайное количество распределений, а затем экспортирует их. Измените путь к выходному файлу, чтобы он работал для вас:
void Main()
{
var dataGenerator = new DataGenerator();
var allocations = dataGenerator.Generate();
var xlFile = new FileInfo(@"d:\so-test.xlsx");
if (xlFile.Exists)
{
xlFile.Delete();
}
using(var xl = new ExcelPackage(xlFile))
{
FundAllocationsPrinter.Print(xl, allocations);
xl.Save();
}
}
// Define other methods and classes here
public static class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
var rowNumber = 1;
int tableIndex = 0;
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
foreach (var ag in allocation)
{
tableIndex += 1;
Console.WriteLine(tableIndex);
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
var count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109, [Allocations])";
table.TableStyle = TableStyles.Dark10;
}
//Account for the table header
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + rowNumber].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + rowNumber].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + rowNumber].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + rowNumber].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + rowNumber].Value = ac.UsdEmv;
wsSheet1.Cells["F" + rowNumber].Value = Math.Round(ac.GroupPercent, 2);
wsSheet1.Cells["G" + rowNumber].Value = Math.Round(ac.WEIGHT_WITH_EQ, 2);
rowNumber++;
}
//Account for the table footer
rowNumber += rowAdderForFooter;
}
}
}
public class FIRMWIDE_MANAGER_ALLOCATION
{
public FIRMWIDE_MANAGER_ALLOCATION(string name, Random rnd)
{
Name = name;
MANAGER_STRATEGY_NAME = "strategy name";
MANAGER_FUND_NAME = "fund name";
PRODUCT_NAME = "product name";
EVAL_DATE = DateTime.Now;
UsdEmv = (decimal)rnd.NextDouble() * 100000000;
GroupPercent = (decimal)rnd.NextDouble() * 100;
WEIGHT_WITH_EQ = 0;
}
public string Name { get; set; }
public string MANAGER_STRATEGY_NAME { get; set; }
public string MANAGER_FUND_NAME { get; set; }
public string PRODUCT_NAME { get; set; }
public DateTime EVAL_DATE { get; set; }
public decimal UsdEmv { get; set; }
public decimal GroupPercent { get; set; }
public decimal WEIGHT_WITH_EQ { get; set; }
}
public class DataGenerator
{
public static Random rnd = new Random();
public ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> Generate()
{
var data = new List<FIRMWIDE_MANAGER_ALLOCATION>();
var itemCount = rnd.Next(1, 100);
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
var name = Path.GetRandomFileName();
data.AddRange(GenerateItems(name));
}
return data.ToLookup(d => d.Name, d => d);
}
private IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> GenerateItems(string name)
{
var itemCount = rnd.Next(1,100);
var items = new List<FIRMWIDE_MANAGER_ALLOCATION>();
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
items.Add(new FIRMWIDE_MANAGER_ALLOCATION(name, rnd));
}
return items;
}
}