lunes, 12 de octubre de 2009

C# – Word – Utilización de Tablas

 

Introducción

El hacer uso de las librerías de Interop de Office puede llega a ser complejo en ciertas circunstancias, mas cuando se necesita hacer uso de posiciones relativas a otros elemento en un documento, es por ello que en este ejemplo intento reflejar como hacer uso de tablas en Word manipulándolas desde código.

Pasos previos

Se debe remarcar que para que estos ejemplos puedan ser ejecutados es necesario contar un Office instalado en el equipo, ya que las librerías de Interop requieres que se encuentren presentes los componente COM que utilizan, y es necesario que Word sea instalado para ello.

Notarán en el código en sus primeras líneas la inclusión de un alias en el using para poder referenciar a la funcionalidad de Office Word

using Office = Microsoft.Office.Interop.Word;
Introducir una tabla dentro de otra

La primera aproximación que tendremos con las tablas será para utilizarlas de forma anidadas, o sea una tabla dentro de otra.

Para ellos contaremos con el siguiente código:

        private void btnTabladentroTabla_Click(object sender, EventArgs e)
        {
            Office.ApplicationClass app = new Office.ApplicationClass();
            
            //
            // Agrego un nuevo documento a la aplicacion Word
            //
            object missing = System.Reflection.Missing.Value;

            Office.Document doc = app.Documents.Add(ref missing, ref missing,
                                 ref missing, ref missing);



            //
            // Adiciono una tabla sobre el documento
            //
            object DefaultTableBehavior = Office.WdDefaultTableBehavior.wdWord9TableBehavior;
            object WdAutoFitBehavior = Office.WdAutoFitBehavior.wdAutoFitWindow;

            Office.Table table1 = doc.Tables.Add(app.Selection.Range, 4, 3, ref DefaultTableBehavior,ref WdAutoFitBehavior);
            
            //
            // Inserto al segunda tabla, pero a esta le indicare que el range donde debe
            // incluirse esta dentro de la primer tabla, mas precisamente en la primer columna
            // segunda fila
            //
            Office.Table table2 = doc.Tables.Add(table1.Cell(2,1).Range, 2, 3, ref DefaultTableBehavior,ref WdAutoFitBehavior);

            //
            // Agrego una fila nuevo a la tabla
            //
            table2.Rows.Add(ref missing);

            //
            // Guardo el archivo
            //
            object fileNameSave = Path.Combine(Application.StartupPath, "ArchivoGenerado.doc");

            doc.SaveAs(ref fileNameSave, ref missing, ref missing, 
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing, 
                        ref missing, ref missing, ref missing, ref missing, ref missing);


            doc.Close(ref missing, ref missing, ref missing);

            MessageBox.Show("El documento se generó correctamente");

        }

El código de por si, al seguirlo paso a paso esta bastante claro en las operaciones que realiza, pero por ahí hay algunos puntos a remarcar.

Uno de ellos es la línea 23 se notará que el rango utilizado en la creación de la tabla incluye la selección de una celda de la tabla creada previamente, este es el punto clave para insertar una tabla dentro de otra, la sección correcta del lugar donde ubicarla.

Una aspecto bastante raro que me encontré cuando realizaba las pruebas es que por mas que hacia uso de los parámetros NumRows la tabla interna siempre aparecía con una sola fila, es por ellos que adicione una línea de código que inserta una fila adicional. Esto se visualiza en la línea 27 del código.

Insertar una tabla en medio de otras dos

Aquí también es importante jugar con una correcta selección de los objetos, y ubicar correctamente la posición para insertar la nueva tabla.

        private void btnTablaEntreTablas_Click(object sender, EventArgs e)
        {
            Office.ApplicationClass app = new Office.ApplicationClass();

            //
            // Realizo al apertura de un archivo existente, el cual contendra 4 tablas
            //
            object missing = System.Reflection.Missing.Value;
            object fileNameAbrir = Path.Combine(Application.StartupPath, "ArchivoOriginal.doc");

            Office.Document doc = app.Documents.Open(ref fileNameAbrir, ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing);

            //
            // Selecciono la segunda tabla del documento
            //
            doc.Tables[2].Range.Select();
 
            //
            // de la seleccion anterio me desplazo un posicion hacia abajo
            //
            object moveUnit = Office.WdUnits.wdLine;
            object moveLength = 1;

            app.Selection.MoveDown(ref moveUnit, ref moveLength, ref missing);

            //
            // Agrego un espacio, o entrer, para generar la separacion para la proxima tabla
            //
            app.Selection.TypeParagraph();


            //
            // Inserto a la nueva tabla
            //
            object DefaultTableBehavior = Office.WdDefaultTableBehavior.wdWord9TableBehavior;
            object WdAutoFitBehavior = Office.WdAutoFitBehavior.wdAutoFitWindow;

            Office.Table table1 = doc.Tables.Add(app.Selection.Range, 4, 3, ref DefaultTableBehavior, ref WdAutoFitBehavior);

            Office.Range cellrange = table1.Cell(1, 1).Range;
            cellrange.Text = "Tabla Intermedia";

            //
            // Guardo el documento
            //
            object fileNameSave = Path.Combine(Application.StartupPath, "ArchivoGenerado.doc");

            doc.SaveAs(ref fileNameSave, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing, ref missing);


            doc.Close(ref missing, ref missing, ref missing);

            MessageBox.Show("El documento se genero correctamente");

        }

En este ejemplo a diferencia del anterior, se parte de un documento ya existente, al cuál se le agregara una tabla intermedia entre otras dos tablas.

En la línea 17 se visualiza como se selecciona la tabla debajo de la cual se quiere insertar la nueva. Las siguientes líneas 21-27 son importantes para posicionar el cursor de forma correcta.

Debe remarcarse como se hace uso de la propiedad Selection del objeto Application, en este caso no se hace uso del Document. El objetivo, una vez que se tiene la tabla seleccionada, se baja una posición con el cursor, y luego agregar un enter o párrafo, para lograr separar las tablas.

Sino se agrega un párrafo intermedio entonces las tablas se crearán unidas, no logrando el efecto deseado.

[C#]
 

2 comentarios:

  1. Tal vez esta no sea la entrada más adecuada para realizar esta pregunta, pero como está bajo la etiqueta Office, y mencionás el uso de las librerías de Interop...
    El asunto es que a partir de un datatable genero un excel. (El usuario hace un click en un botón, y descarga el .xls generado)
    Lo hago mediante
    Response.ContentType = "application/vnd.ms-excel"

    Y luego construyo la tabla mediante Response.write adecuadamente colocados (i.e.:
    Response.Write("<html>")
    Response.Write("<body>")
    Response.Write("<table>")
    Response.Write("<tr>")
    ...etc...
    ... for adecuados...
    Response.Write(row.Item(i))
    ...etc...
    Response.Write("</html>")

    El resultado en cuanto a los datos es el deseado, pero me gustaría poder usar otro mecanismo diferente, que me permita asociar un template al excel generado. En el template ya tendría predefinido el nombre y tipo de las columnas, etc. (Solucionaría inconvenientes al usuario final. Se evitaría que no tenga que andar alineando nada, etc)
    Y de repente ver también si se puede evitar la tosquedad de esos "Response.write"
    La verdad que no he podido dar con una solución más adecuada que la que la provisoria usada ahora.
    Muchas gracias

    ResponderEliminar
  2. La solución a parte de lo planteado está en:

    http://www.aspsnippets.com/Articles/Export-DataSet-or-DataTable-to-Word-Excel-PDF-and-CSV-Formats.aspx

    Con eso se evitan los torpes Response.write, y se tiene más control sobre el formateo de datos.
    (Notar el uso de un Gridview dummy.)
    Hay artículos asociados que amplían el uso de esta técnica.
    En cuanto al uso del template, todavía no me queda claro, pero con el ejemplo mencionado se puede alcanzar un mayor control del formato.

    ResponderEliminar