2009-02-11 22 views
45

Actualmente estoy tratando de escribir datos en una matriz de objetos a un rango en Excel usando el siguiente código, donde objData es sólo una matriz de cadenas:escritura matriz de rango de Excel

private object m = System.Type.Missing; 
object[] objData = getDataIWantToWrite(); 

Range rn_Temp; 
rn_Temp = (Range)XlApp.get_Range(RangeName, m); 
rn_Temp = rn_Temp.get_Resize(objData.GetUpperBound(), 1); 
rn_Temp.value2 = objData; 

Esta muy cerca funciona, el problema es que el rango se llena pero cada celda obtiene el valor del primer elemento en objData.

La inversa funciona, es decir

private object m = System.Type.Missing; 
object[] objData = new object[x,y] 

Range rn_Temp; 
rn_Temp = (Range)XlApp.get_Range(RangeName, m); 
rn_Temp = rn_Temp.get_Resize(objData.GetUpperBound(), 1); 
objData = (object[])rn_Temp.value2; 

devolvería una matriz que contiene todos los valores de la hoja de trabajo, así que no estoy seguro de por qué la lectura y la asignación de trabajo diferente.

¿Alguien ha hecho esto con éxito? Actualmente estoy escribiendo la matriz celda por celda, pero tiene que lidiar con lotes (> 50,000) de filas y esto consume mucho tiempo.

+7

La respuesta seleccionada no lo hace inmediatamente obvio, por lo que si alguna vez alguien más tiene este problema, en definitiva, la solución es que necesita asignar una matriz 2D ('objeto [,]') aunque sus datos podrían caber en una sola dimensión. – zneak

+1

[muy útil] (http://blogs.msdn.com/b/eric_carter/archive/2004/05/04/126190.aspx) –

Respuesta

77

Este es un extracto del método mío, que convierte una DataTable (la variable dt) en una matriz y luego escribe la matriz en un rango en una hoja de cálculo (wsh var). También puede cambiar la variable topRow por la fila en la que desee colocar la matriz de cadenas.

 object[,] arr = new object[dt.Rows.Count, dt.Columns.Count]; 
     for (int r = 0; r < dt.Rows.Count; r++) 
     { 
      DataRow dr = dt.Rows[r]; 
      for (int c = 0; c < dt.Columns.Count; c++) 
      { 
       arr[r, c] = dr[c]; 
      } 
     } 

     Excel.Range c1 = (Excel.Range)wsh.Cells[topRow, 1]; 
     Excel.Range c2 = (Excel.Range)wsh.Cells[topRow + dt.Rows.Count - 1, dt.Columns.Count]; 
     Excel.Range range = wsh.get_Range(c1, c2); 

     range.Value = arr; 

Por supuesto que no es necesario utilizar un DataTable intermedia como lo hice, el extracto de código es sólo para demostrar cómo una matriz se puede escribir en la hoja de trabajo en una sola llamada.

+0

Interesante: eso es exactamente lo que quiero hacer, pero no puedo acceder a la propiedad Value del objeto range. Solo puedo acceder a Value2, y creo que ese puede ser el problema. ¿Alguna idea de cómo puedo acceder a la propiedad Value? ¿Qué interoperaciones estás usando? –

+0

Importado de la biblioteca de tipos de Excel (pasa a ser Office 2003 en mi caso). Es decir, no estoy utilizando los conjuntos de interoperabilidad primarios. Acabo de agregar la referencia a Excel COM tlb en VS. Ahora bien, no puedo recordar la razón exacta para no usar los PIA. –

+2

Uso range.set_Value (Missing.Value, objectArray); Debe asegurarse de que la matriz tenga el mismo tamaño que el rango. –

4

Puede poner sus datos en un conjunto de registros y usar Excel's CopyFromRecordset Method - es mucho más rápido que rellenar celda por celda.

Puede crear un conjunto de registros a partir de un conjunto de datos usando this code. Tendrá que hacer algunas pruebas para ver si usar este método es más rápido que lo que está haciendo actualmente.

+0

Esto puede ser una pregunta tonta, pero ¿dónde puedo encontrar una clase Recordset en C# ? ¿O es que sabes si hay otra clase que puedo aprobar y que funcionará? ¡Muchas gracias! –

+1

Agregar referencia al ensamblaje ADODB versión 7.0.3300.0. usando ADODB; ADODB tiene el tipo "Recordset" –

9

Gracias por los consejos chicos - el argumento Value vs Value2 me dio un conjunto diferente de resultados de búsqueda que me ayudaron a darme cuenta de cuál es la respuesta. A propósito, la propiedad Value es una propiedad parametrizada, a la que se debe acceder a través de un descriptor de acceso en C#. Estos se llaman get_Value y set_Value, y toman un valor enum opcional. Si alguien está interesado, this explains it nicely.

Sin embargo, es posible hacer la asignación a través de la propiedad Value2, que es preferible ya que la documentación de interoperabilidad recomienda el uso de los métodos get_Value y set_Value, por razones que no entiendo.

La clave parece ser la dimensión de la matriz de objetos. Para que la llamada funcione, la matriz debe declararse como bidimensional, incluso si solo está asignando datos unidimensionales.

Declaro mi matriz de datos como object[NumberofRows,1] y funcionó la llamada de asignación.

+0

gracias, parece que si se le da una matriz 1D, siempre la escribirá horizonalmente (incluso con un rango vertical, en cuyo caso escribe el primer elemento repetidamente). para obtener una matriz para escribir verticalmente, debes hacer lo que dijiste y convertirla en una matriz '[n, 1]'. muchas gracias porque probablemente nunca lo hubiera descubierto yo mismo. –

0

Por alguna razón, la conversión a una matriz bidimensional no funcionó para mí.Pero el siguiente enfoque hizo:

public void SetRow(Range range, string[] data) 
{ 
    range.get_Resize(1, data.Length).Value2 = data; 
} 
1

El tipo de definición de la matriz parece la clave: En mi caso se trata de una matriz de una dimensión de 17 artículos que tienen que convertir en una matriz de dos dimensiones

Defintion de columnas: objeto [,] Matriz = objeto nuevo [17, 1];

Definición de filas objeto [,] Matriz = objeto nuevo [1,17];

El código para value2 es en ambos casos el mismo Excel.Cantidad de celda = activeWorksheet.get_Range (Range); cell.Value2 = Matriz;

LG Georg

1

En mi caso, el programa consulta la base de datos que devuelve un DataGridView. Luego copio eso en una matriz. Obtengo el tamaño de la matriz recién creada y luego escribo la matriz en una hoja de cálculo de Excel. Este código genera más de 5000 líneas de datos en aproximadamente dos segundos.

//private System.Windows.Forms.DataGridView dgvResults; 
dgvResults.DataSource = DB.getReport(); 

Microsoft.Office.Interop.Excel.Application oXL; 
Microsoft.Office.Interop.Excel._Workbook oWB; 
Microsoft.Office.Interop.Excel._Worksheet oSheet; 
try 
{ 
    //Start Excel and get Application object. 
    oXL = new Microsoft.Office.Interop.Excel.Application(); 
    oXL.Visible = true; 

    oWB = (Microsoft.Office.Interop.Excel._Workbook)(oXL.Workbooks.Add("")); 
    oSheet = (Microsoft.Office.Interop.Excel._Worksheet)oWB.ActiveSheet; 

    var dgArray = new object[dgvResults.RowCount, dgvResults.ColumnCount+1]; 
    foreach (DataGridViewRow i in dgvResults.Rows) 
    { 
     if (i.IsNewRow) continue; 
     foreach (DataGridViewCell j in i.Cells) 
     { 
      dgArray[j.RowIndex, j.ColumnIndex] = j.Value.ToString(); 
     } 
    } 

    Microsoft.Office.Interop.Excel.Range chartRange; 

    int rowCount = dgArray.GetLength(0); 
    int columnCount = dgArray.GetLength(1); 
    chartRange = (Microsoft.Office.Interop.Excel.Range)oSheet.Cells[2, 1]; //I have header info on row 1, so start row 2 
    chartRange = chartRange.get_Resize(rowCount, columnCount); 
    chartRange.set_Value(Microsoft.Office.Interop.Excel.XlRangeValueDataType.xlRangeValueDefault, dgArray); 


    oXL.Visible = false; 
    oXL.UserControl = false; 
    string outputFile = "Output_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx"; 

    oWB.SaveAs("c:\\temp\\"+outputFile, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookDefault, Type.Missing, Type.Missing, 
     false, false, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, 
     Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); 

    oWB.Close(); 
} 
catch (Exception ex) 
{ 
    //... 
} 
0

cuando se quiere escribir una matriz de 1D en una hoja de Excel hay que trasladarla y que no tiene que crear una matriz 2D con 1 columna ([n, 1]) como ¡Leí arriba! Aquí es un ejemplo de código:

wSheet.Cells(RowIndex, colIndex).Resize(RowsCount,).Value = _excel.Application.transpose(My1DArray) 

tuvo un buen día, Gilles

Cuestiones relacionadas