Как указать формат для отдельных ячеек с помощью Excel.Range.set_Value ()

Когда я пишу всю таблицу в лист 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. Я хочу, чтобы для каждой ячейки был указан другой формат, указанный во внутреннем цикле.


person Drew Shafer    schedule 22.02.2010    source источник


Ответы (3)


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

// 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 минут!

person Drew Shafer    schedule 23.02.2010
comment
Хотя я принял свой ответ, я решил, что использование автоматизации Excel для записи файлов - плохой план. В дальнейшем я использую библиотеку ExcelPackage для прямой записи файлов .xlsx. - person Drew Shafer; 04.03.2010
comment
+ один за отличный код; ) Я не знал, что вы можете использовать formatSpecifier для установки диапазона ячеек. - person Leo Gurdian; 03.06.2017

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

person Stan R.    schedule 22.02.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, вы можете изменить формат ячейки как хотите.

person JDMX    schedule 22.02.2010
comment
Я понимаю, что это выполнит свою работу, но я стараюсь свести количество вызовов в Excel Interop к абсолютному минимуму. Применение форматирования во внутреннем цикле будет работать, но будет очень медленно. - person Drew Shafer; 23.02.2010