2010-02-22 22 views
7

Cuando escribo una tabla completa en una hoja de cálculo de Excel, sé que debo trabajar con todo un Rango a la vez en lugar de escribir en celdas individuales. Sin embargo, ¿hay alguna manera de especificar el formato cuando estoy completando la matriz que voy a exportar a Excel?Cómo especificar el formato de celdas individuales con Excel.Range.set_Value()

Aquí es lo que hago ahora:

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); 

Ahora supongamos que yo quiero algunos de esos números para ser formateado como porcentajes. Sé que puedo volver celda por celda y cambiar el formato, pero eso parece frustrar todo el propósito de usar una sola llamada Range.set_Value(). ¿Puedo hacer que mi estructura rangeData [,] incluya información de formato, para que cuando llame a set_Value(), las celdas estén formateadas de la forma que yo las quiera?

Para aclarar, sé que puedo establecer el formato para todo el objeto Excel.Range. Lo que quiero es tener un formato diferente especificado para cada celda, especificado en el ciclo interno.

Respuesta

2

Así que aquí está la mejor "solución" que he encontrado hasta ahora. No es el nirvanna que estaba buscando, pero es mucho, mucho más rápido que establecer el formato para cada celda individualmente.

// 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(); 
     } 
    } 
} 

Básicamente lo que ocurre es que guardo una lista de la información de formato para cada celda de NumberFormatList (cada elemento también contiene la dirección de estilo A1 de la gama que se aplica a). La idea original era que para cada formato distinto en la hoja de trabajo, debería ser capaz de construir un Excel.Range de solo esas celdas y aplicar el formato a ese rango en una sola llamada. Esto reduciría el número de accesos a NumberFormat desde (potencialmente) miles hasta solo unos pocos (sin importar cuántos formatos diferentes tenga).

Me encontré con un problema, sin embargo, porque aparentemente no puede construir un rango de una lista arbitrariamente larga de celdas. Después de algunas pruebas, encontré que el límite está entre 50 y 100 celdas que se pueden usar para definir un rango arbitrario (como en range.get_Range ("A1, B1, C1, A2, AA5, .....") Así que una vez que he obtenido la lista de todas las celdas para aplicar un formato, tengo un ciclo while() final que aplica el formato a 50 de esas celdas a la vez.

Esto no es ideal, pero todavía reduce el número de accesos a NumberFormat por un factor de hasta 50, lo cual es significativo. La construcción de mi hoja de cálculo sin ninguna información de formato (solo usando range.set_Value()) tarda unos 3 segundos. Cuando aplico los formatos 50 celdas a un tiempo, que se alarga a unos 10 segundos. Cuando aplico la información de formato individualmente a cada celda, la hoja de cálculo tarda más de 2 minutos en terminar de construirse!

+0

Aunque acepté mi propia respuesta, lo que he decidido es que usar la automatización de Excel para escribir archivos es un mal plan. En el futuro, estoy usando la biblioteca ExcelPackage para escribir directamente.xlsx files –

+0

+ uno por ser una buena pieza de código;) No sabía que podrías usar un FormatSpecifier para establecer un rango de celdas. –

2

puede aplicar un formato en el rango, y luego rellenarlo con los valores que no se puede especificar el formato en que object[,] gama

0

Se aplica el formato a cada célula individual dentro del bucle interno a través de

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

Una vez que tenga r2, puede cambiar el formato de la celda de la forma que desee.

+0

Me doy cuenta de que obtendrá el trabajo hecho, pero estoy tratando de mantener el número de llamadas en la Interoperación de Excel a un mínimo absoluto. Aplicar el formateo en el ciclo interno funcionaría, pero sería demasiado lento. –

Cuestiones relacionadas