如何使用Excel.Range.set_Value()指定单个单元格的格式

时间:2010-02-22 21:36:31

标签: c# .net excel interop spreadsheet

当我将整个表写入excel工作表时,我知道立即使用整个Range,而不是写入单个单元格。但是,有没有办法指定格式,因为我正在填充我要导出到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 个答案:

答案 0 :(得分:2)

您可以对范围应用格式,然后使用您无法在object[,]数组中指定格式的值填充它

答案 1 :(得分:2)

所以这是迄今为止我发现的最好的“解决方案”。它不是我想要的nirvanna,但它比单独设置每个单元格的格式要快得多。

// 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 :(得分:0)

通过

将格式应用于内循环中的每个单独的单元格
for(int r = 0; r < rows; r++)
{
    for(int c = 0; c < cols; c++)
    {
       Excel.Range r2 = sheet.Cells( r, c );
       r2.xxxx = "";
    }
}

获得r2后,您可以按照自己的方式更改单元格格式。