DataGrid para imprimir con Crystal Reports en C# y VB

Desde un archivo XML se rellena un DataGrid cuyas columnas se crean por código configurando parámetros como la anchura de cada columna, con filtros de selección de registros basados en sentencias SQL y 2 opciones diferentes para imprimir los datos: mediante un informe de Crystal Reports o efectuando una transformación XSL al archivo origen de los datos para obtener una página HTML con formato, en C# y VB

El formulario principal tiene un DataGrid con las 4 columnas de la tabla obtenida desde un archivo XML que tiene 4 elementos: Id, Apellidos, Nombre y Cargo.

<?xml version="1.0" encoding="UTF-8"?>
<dataroot>
  <Tabla>
    <Id>1</Id>
    <Apellidos>Alba Cabello</Apellidos>
    <Nombre>Víctor</Nombre>
    <Cargo>Representante de compras</Cargo>
  </Tabla>
  <Tabla>
    <Id>2</Id>
    <Apellidos>Alberca Luis</Apellidos>
    <Nombre>Luis</Nombre>
    <Cargo>Ayudante de compras</Cargo>
  </Tabla>
  <Tabla>
    <Id>3</Id>
    <Apellidos>Alberto Sánchez</Apellidos>
    <Nombre>Cristina</Nombre>
    <Cargo>Representante de compras</Cargo>
  </Tabla>
  <Tabla>
    <Id>4</Id>
    <Apellidos>Alvarez Luis</Apellidos>
    <Nombre>Juan</Nombre>
    <Cargo>Representante de compras</Cargo>
  </Tabla>
  <Tabla>
    <Id>5</Id>
    <Apellidos>Arregui Etxegui</Apellidos>
    <Nombre>María</Nombre>
    <Cargo>Representante de compras</Cargo>
  </Tabla>
</dataroot>

Para generar por código las columnas del DataGrid se crea un DataGridTableStyle al que se asigna el nombre de la tabla origen de los datos, se instancia un objeto DataGridTextBoxColumn y con ese objeto se van generando las columnas asignando a cada una su origen (una columna de la tabla) y añadiéndolas al DataGridTableStyle. La tabla es también el origen de datos del DataGrid por medio de un DataSet que funciona como almacén desconectado de los datos.

//DataSet: contiene una copia de los datos en esquema XML, independiente del proveedor, con sus elementos tablas y relaciones
datos = new DataSet();
//Rellenar el DataSet desde el archivo XML
datos.ReadXml("Clientes.xml");

//crear un objeto para estilos en el Datagrid
DataGridTableStyle estilo = new DataGridTableStyle();
estilo.MappingName = "Tabla";

Crear (objetos del tipo DataGridTextBoxColumn para cada columna de la tabla del Datagrid);
DataGridTextBoxColumn columna = new DataGridTextBoxColumn();

//Esta es la primera columna
columna = new DataGridTextBoxColumn();
columna.TextBox.MaxLength = 10;
columna.HeaderText = "Id";
//columna del Dataset enlazada con esta columna del Datagrid
columna.MappingName = "Id";
columna.Width = 60;
//texto que se muestra cuando la columna tiene valor null
columna.NullText = "";
//añadir la columna a los estilos del Datagrid
estilo.GridColumnStyles.Add(columna);

//Añadir el estilo personalizado a la colección de estilos de tablas del Datagrid
this.Grid.TableStyles.Add(estilo);

//asignar Dataset al Datagrid
this.Grid.DataSource = this.datos.Tables("Tabla");

Para filtrar los registros se utiliza el objeto DataView que permite personalizar la vista que se muestra de una tabla, con una sentencia SQL de selección que utiliza el texto escrito en un TextBox para buscar en la columna elegida desde un ComboBox con los ítems Nombre, Apellidos y Cargo.

DataView vista = new DataView();
vista.Table = datos.Tables["Tabla"];
switch (CbFiltrar.Text)
{
    case "Nombre":
	//filtro SQL: registros que empiecen por el texto escrito en el TextBox
	vista.RowFilter = "Nombre LIKE '" + this.TxtFiltro.Text + "%'";
	vista.Sort = "Nombre";
	//nuevo origen del DataDrid: la vista personalizada
	this.Grid.DataSource = vista;
	break;
    case "Apellidos":
	//filtro SQL: registros que empiecen por el texto escrito en el TextBox
	vista.RowFilter = "Apellidos LIKE '" + this.TxtFiltro.Text + "%'";
	vista.Sort = "Apellidos";
	//nuevo origen del DataDrid: la vista personalizada
	this.Grid.DataSource = vista;
	break;
    case "Cargo":
	//filtro SQL: registros que empiecen por el texto escrito en el TextBox
	vista.RowFilter = "Cargo LIKE '" + this.TxtFiltro.Text + "%'";
	vista.Sort = "Cargo";
	//nuevo origen del DataDrid: la vista personalizada
	this.Grid.DataSource = vista;
	break;
}
//recargar el DataGrid
this.Grid.Update();

La actualización del origen de los datos con los cambios que se hayan efectuado se consigue escribiendo el DataSet al archivo XML y volviendo a cargarlo de nuevo.

//escribir el DataSet al archivo XML y recargarlo de nuevo desde ese archivo
datos.WriteXml("Clientes.xml");
datos.Clear();
datos.ReadXml("Clientes.xml");

Imprimir con Crystal Reports

Es necesario instalar Crystal Reports (CR) para utilizar con Visual Studio (VS) (SAP Crystal Reports for Visual Studio). La versión de CR ha de ser adecuada a la versión de VS. Por ejemplo en el momento de escribir este artículo para VS 2019 se descarga la versión 25 de CR. Al terminar la instalación un diálogo pregunta si se debe instalar también el entorno de ejecución de 64 bits, elige esta opción si deseas probar aplicaciones de VS con CR compiladas para 64 bits. Hay que agregar al proyecto 4 referencias:

  • CrystalDecissions.CrystalReports.Engine
  • CrystalDecissions.ReportSource
  • CrystalDecissions.Shared
  • CrystalDecissions.Windows.Forms

Se encuentran en la carpeta:

C:\Program Files (x86)\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Common\SAP BusinessObjects Enterprise XI 4.0\win32_x86\dotnet

Cuando estas tareas están realizadas, hay que añadir al proyecto el componente Crystal Reports que se encuentra en el grupo Reporting, se configura su origen de datos que es el archivo XML y se formatea a nuestro gusto para obtener una impresión correcta. Este componente CR está incluido en un segundo formulario que se abre desde el formulario principal.

//Imprimir abriendo el segundo formulario con un informe de Crystal Reports
private void BtPrintXsl_Click(object sender, EventArgs e)
{
    Informe2 ventana = new Informe2();
    ventana.Show();
}
//en el segundo formulario
public class Informe2 : System.Windows.Forms.Form
{

	#region  Código generado por el Diseñador de Windows Forms
	internal Imprimir_xml.Informe1 Informe11;
	this.CrystalReportViewer1 = new CrystalDecisions.Windows.Forms.CrystalReportViewer();
	this.Informe11 = new Imprimir_xml.Informe1();
	//
	//CrystalReportViewer1
	//
	this.CrystalReportViewer1.Dock = System.Windows.Forms.DockStyle.Fill;
	this.CrystalReportViewer1.Name = "CrystalReportViewer1";
	this.CrystalReportViewer1.ReportSource = this.Informe11;
	this.CrystalReportViewer1.ToolPanelView = CrystalDecisions.Windows.Forms.ToolPanelViewType.None;
	//
	//Informe2
	//
	this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
	this.ClientSize = new System.Drawing.Size(884, 514);
	this.Controls.Add(this.CrystalReportViewer1);
	this.Name = "Informe2";
	this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
	this.Text = "Clientes";
	#endregion

	private void Informe2_Load(System.Object sender, System.EventArgs e)
	{
		this.CrystalReportViewer1.Zoom(1);
	}

}

Imprimir con transformación XSL del archivo XML para obtener HTML

La otra opción para imprimir los datos consiste en utilizar un archivo XSL en el que se configura la presentación de los datos XML para obtener una página web formateada de acuerdo con esa presentación gracias a la clase XslCompiledTransform del espacio de nombres System.Xml.Xsl, su método Load que carga un archivo XSL y su método Transform que carga el archivo XML, le aplica la transformación XSL y genera el archivo HTML. Posteriormente se utiliza la clase ProcessStartInfo para abrir la página web con su programa asociado en Windows.

string ruta1 = "Clientes.xsl";
string ruta2 = "Clientes.xml";
string ruta3 = "Clientes.htm";
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(ruta1);
xslt.Transform(ruta2, ruta3);

//ProcessStartInfo se utiliza para iniciar procesos
ProcessStartInfo abrirDoc = new ProcessStartInfo();
abrirDoc.FileName = Application.StartupPath + "\\Clientes.htm";
//modo de apertura de archivo (también puede ser edit, print...)
abrirDoc.Verb = "open";
//abrir en ventana normal
abrirDoc.WindowStyle = ProcessWindowStyle.Normal;
//Abrir el archivo mediante su programa asociado en Windows
Process.Start(abrirDoc);

El archivo XSL utilizado para la transformación es así:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <head>
      <title>Clientes</title>
    </head>
    <html>
      <body style="font-family: Arial; font-size: 10pt;">
        <div align="center">
          <h3>
            <u>Agenda de clientes</u>
          </h3>
          <table width="600" border="0">
            <tr style="font-family: Arial; font-size: 10pt;">
              <td width="30" align="left">
                <b>
                  <font color="black">Id</font>
                </b>
              </td>
              <td width="180" align="left">
                <b>
                  <font color="black">Apellidos</font>
                </b>
              </td>
              <td width="140" align="left">
                <b>
                  <font color="black">Nombre</font>
                </b>
              </td>
              <td width="180" align="left">
                <b>
                  <font color="black">Cargo</font>
                </b>
              </td>
            </tr>
            <tr>
              <td align="center" colspan="4">
                <HR></HR>
              </td>
            </tr>
            <xsl:for-each select="dataroot/Tabla">
              <xsl:sort select="ES" order="ascending"></xsl:sort>
              <tr bgcolor="white" style="font-family: Arial; font-size: 10pt;">
                <td align="left">
                  <font color="black">
                    <xsl:value-of select="Id"></xsl:value-of>
                  </font>
                </td>
                <td align="left">
                  <font color="black">
                    <xsl:value-of select="Apellidos"></xsl:value-of>
                  </font>
                </td>
                <td align="left">
                  <font color="black">
                    <xsl:value-of select="Nombre"></xsl:value-of>
                  </font>
                </td>

                <td align="left">
                  <font color="black">
                    <xsl:value-of select="Cargo"></xsl:value-of>
                  </font>
                </td>
              </tr>
              <tr>
                <td align="left" colspan="4">
                  <HR></HR>
                </td>
              </tr>
            </xsl:for-each>
          </table>
        </div>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Puedes descargar un archivo ZIP con las aplicaciones C# y VB.