Как указать формат для отдельных ячеек с помощью Excel.Range.set_Value () - PullRequest
7 голосов
/ 23 февраля 2010

Когда я записываю всю таблицу в таблицу Excel, я знаю, что нужно работать с целым диапазоном одновременно, а не записывать в отдельные ячейки. Однако есть ли способ указать формат при заполнении массива, который я собираюсь экспортировать в Excel?

Вот что я делаю сейчас:

object MissingValue = System.Reflection.Missing.Value;
Excel.Application excel = new Excel.Application();
int rows = 5;
int cols = 5;
int someVal;

Excel.Worksheet sheet = (Excel.Worksheet)excel.Workbooks.Add(MissingValue).Sheets[1];
Excel.Range range = sheet.Range("A1", sheet.Cells(rows,cols));
object[,] rangeData = new object[rows,cols];
for(int r = 0; r < rows; r++)
{
    for(int c = 0; c < cols; c++)
    {
        someVal = r + c;
        rangeData[r,c] = someVal.ToString();
    }
}
range.set_Value(MissingValue, rangeData);

Теперь предположим, что я хочу, чтобы некоторые из этих чисел были отформатированы в процентах. Я знаю, что могу вернуться к порядку за ячейкой и изменить форматирование, но это, похоже, лишает смысла использование единственного вызова Range.set_Value (). Могу ли я сделать, чтобы моя структура rangeData [,] включала информацию о форматировании, чтобы при вызове set_Value () ячейки форматировались так, как я хочу?

Чтобы уточнить, я знаю, что могу установить формат для всего объекта Excel.Range. Я хочу, чтобы для каждой ячейки был указан другой формат, указанный во внутреннем цикле.

Ответы [ 3 ]

2 голосов
/ 23 февраля 2010

Итак, вот лучшее «решение», которое я нашел до сих пор. Это не та нирванна, которую я искал, но она намного, намного быстрее, чем настройка формата для каждой ячейки отдельно.

// 0-based indexes
static string RcToA1(int row, int col)
{
    string toRet = "";
    int mag = 0;
    while(col >= Math.Pow(26, mag+1)){mag++;}
    while (mag>0)
    {
        toRet += System.Convert.ToChar(64 + (byte)Math.Truncate((double)(col/(Math.Pow(26,mag)))));
        col -= (int)Math.Truncate((double)Math.Pow(26, mag--));
    }
    toRet += System.Convert.ToChar(65 + col);
    return toRet + (row + 1).ToString();
}

static Random rand = new Random(DateTime.Now.Millisecond);
static string RandomExcelFormat()
{
    switch ((int)Math.Round(rand.NextDouble(),0))
    {
        case 0: return "0.00%";
        default: return "0.00";
    }
}


struct ExcelFormatSpecifier
{
    public object NumberFormat;
    public string RangeAddress;
}

static void DoWork()
{
    List<ExcelFormatSpecifier> NumberFormatList = new List<ExcelFormatSpecifier>(0);

    object[,] rangeData = new object[rows,cols];
    for(int r = 0; r < rows; r++)
    {
        for(int c = 0; c < cols; c++)
        {
            someVal = r + c;
            rangeData[r,c] = someVal.ToString();
            NumberFormatList.Add(new ExcelFormatSpecifier
                {
                    NumberFormat = RandomExcelFormat(),
                    RangeAddress = RcToA1(rowIndex, colIndex)
                });
        }
    }
    range.set_Value(MissingValue, rangeData);

    int max_format = 50;
    foreach (string formatSpecifier in NumberFormatList.Select(p => p.NumberFormat).Distinct())
    {
        List<string> addresses = NumberFormatList.Where(p => p.NumberFormat == formatSpecifier).Select(p => p.RangeAddress).ToList();
        while (addresses.Count > 0)
        {
            string addressSpecifier = string.Join(",",     addresses.Take(max_format).ToArray());
            range.get_Range(addressSpecifier, MissingValue).NumberFormat = formatSpecifier;
            addresses = addresses.Skip(max_format).ToList();
        }
    }
}

В основном происходит то, что я храню список информации о формате для каждой ячейки в NumberFormatList (каждый элемент также содержит адрес в стиле A1 диапазона, к которому он применяется). Первоначальная идея заключалась в том, что для каждого отдельного формата на рабочем листе я должен иметь возможность создать Excel.Range только из этих ячеек и применить формат к этому диапазону за один вызов. Это уменьшит количество обращений к NumberFormat с (потенциально) тысяч до нескольких (сколь угодно разных форматов).

Однако я столкнулся с проблемой, потому что вы, очевидно, не можете построить диапазон из произвольно длинного списка ячеек. После некоторого тестирования я обнаружил, что предел находится где-то между 50 и 100 ячейками, которые можно использовать для определения произвольного диапазона (как в range.get_Range («A1, B1, C1, A2, AA5, .....») Поэтому, как только я получил список всех ячеек, к которым нужно применить формат, у меня есть один последний цикл while (), который применяет формат к 50 из этих ячеек одновременно.

Это не идеально, но все равно уменьшает количество обращений к NumberFormat до 50 раз, что существенно. Создание моей таблицы без какой-либо информации о формате (только с использованием range.set_Value ()) занимает около 3 секунд. Когда я применяю форматы по 50 ячеек за раз, это удлиняется примерно до 10 секунд. Когда я применяю информацию о формате отдельно к каждой ячейке, создание таблицы занимает более 2 минут!

2 голосов
/ 23 февраля 2010

Вы можете применить форматирование к диапазону, а затем заполнить его значениями, которые вы не можете указать для форматирования object[,] массив

0 голосов
/ 23 февраля 2010

Вы применяете форматирование к каждой отдельной ячейке во внутреннем цикле с помощью

for(int r = 0; r < rows; r++)
{
    for(int c = 0; c < cols; c++)
    {
       Excel.Range r2 = sheet.Cells( r, c );
       r2.xxxx = "";
    }
}

Если у вас есть r2, вы можете изменить формат ячейки так, как вы хотите.

...