2 formas de imprimir un formulario en C#
Cómo imprimir un formulario en C#
A diferencia
de Visual Basic .NET que tiene el componente PrintForm
(puede imprimir, en tiempo de ejecución, una imagen de un formulario de
Windows) y ahorra bastante código, en C# hay que hacerlo a mano
siguiendo un proceso que resulta muy sencillo de entender. Te voy a
mostrar 2 formas diferentes de hacerlo y cuál de los dos da como
resultado una mejor calidad final:
1.- Imprimiendo el formulario realizando primeramente una captura del
mismo.
2.- Dibujando previamente todos los controles uno a uno.
El componente PrintForm de VB te habilita para imprimir rápidamente una imagen de un formulario tal y como aparece en pantalla sin usar un componente PrintDocument.
1.- Imprimir imagen del formulario
Esta es la forma más sencilla y comentada en la Red. Hay que seguir los siguientes pasos:
- Capturar una instantánea del formulario y convertirla en una imagen Bitmap.
- Dicho bitmap dibujarlo en un documento
- Abrir cuadro de diálogo para seleccionar impresora
- Mostrar, si se quiere, una vista previa
- Imprimir el documento
Antes de ponerte todo el código, voy a ir explicando cada parte:
1.1 - Capturar instantánea del formulario
Puedes hacerlo con este sencillo código:
private void CapturaFormulario() { Size sz = this.Size; bmp = new Bitmap(sz.Width, sz.Height, PixelFormat.Format32bppRgb); Graphics memoryGraphics = Graphics.FromImage(bmp); memoryGraphics.CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, sz, CopyPixelOperation.SourceCopy); //bmp.Save("prueba.bmp", ImageFormat.Bmp); }
Si quitas el comentario de la última línea y guardas la imagen, el resultado será el siguiente:
Este ejemplo tiene el problema de que la imagen a capturar tiene que estar arriba de todas y, en algunos casos, deberás esperar unos milisegundos para que de tiempo a cerrar la que esté sobre ella (por ejemplo, la de selección de impresora). También puedes ver que captura toda la ventana completa.
Esta otra forma la he obtenido de un código visto en microsoft.com:
[System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern long BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); private void CapturaFormulario() { Graphics mygraphics = this.CreateGraphics(); Size sz = this.ClientRectangle.Size; bmp = new Bitmap(sz.Width, sz.Height, mygraphics); Graphics memoryGraphics = Graphics.FromImage(bmp); IntPtr dc1 = mygraphics.GetHdc(); IntPtr dc2 = memoryGraphics.GetHdc(); BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 0, 0, 13369376); mygraphics.ReleaseHdc(dc1); memoryGraphics.ReleaseHdc(dc2); //bmp.Save("prueba.bmp", ImageFormat.Bmp); }
13369376(decimal) = CC0020(hex) = SRCCOPY: Copia el rectángulo de origen directamente en el rectángulo de destino.
Más precisa, y el resultado es:
1.2 - Dibujar el bitmap en un documento a imprimir
Para poder imprimir necesitamos crear un documento y pegar en él todo lo que queramos imprimir. El control PrintDocument es el documento que buscamos. La forma más sencilla de dibujar el bitmap posiblemente sea esta:
e.Graphics.DrawImage(bmp, 0, 0);
Sin embargo, haciéndolo de este modo podemos tener un problema de ajuste de escala. Esto se produce por las diferencias de resolución utilizadas por GDI+ y el objeto Image. Para evitar este comportamiento y mejorar el rendimiento del código, el método DrawImage lo dejo así:
e.Graphics.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height);
1.3 - Seleccionar impresora
Para la selección de la impresora se utiliza PrintDialog:
private PrintDialog Impresora = new PrintDialog();
...
DialogResult Result = Impresora.ShowDialog();
1.4 - Mostrar vista previa
Si quieres puedes mostrar una vista previa:
PrintPreviewDialog VistaPrevia = new PrintPreviewDialog();
...
VistaPrevia.ShowDialog();
1.5 - Imprimir el documento
Es muy sencillo invocando al método Print:
private PrintDocument DocumentoParaImprimir = new PrintDocument();
...
DocumentoParaImprimir.Print();
El método Print produce el evento PrintPage, que es donde pegamos la imagen en el documento.
Todo el código
Para que no tengas que crear nada he intentado hacer un ejemplo con todo incluido. Solamente debes crear un nuevo proyecto y pegar este código:
using System; using System.Drawing; using System.Drawing.Printing; using System.Windows.Forms; namespace imprimirCaptura { public partial class Form1 : Form { private Button btImprimir; private Button btVistaPrevia; private PrintDocument DocumentoParaImprimir = new PrintDocument(); private PrintDialog Impresora = new PrintDialog(); private PrintPreviewDialog VistaPrevia = new PrintPreviewDialog(); private Bitmap bmp; public Form1() { this.btImprimir = new System.Windows.Forms.Button(); this.btImprimir.Location = new System.Drawing.Point(20, 20); this.btImprimir.Size = new System.Drawing.Size(100, 30); this.btImprimir.Text = "Imprimir"; this.btImprimir.Click += new System.EventHandler(this.btImprimir_Click); this.Controls.Add(this.btImprimir); this.btVistaPrevia = new System.Windows.Forms.Button(); this.btVistaPrevia.Location = new System.Drawing.Point(20, 60); this.btVistaPrevia.Size = new System.Drawing.Size(100, 30); this.btVistaPrevia.Text = "Vista Previa"; this.btVistaPrevia.Click += new System.EventHandler(this.btVistaPrevia_Click); this.Controls.Add(this.btVistaPrevia); DocumentoParaImprimir.PrintPage += new PrintPageEventHandler(DocumentoParaImprimir_PrintPage); } void DocumentoParaImprimir_PrintPage(object sender, PrintPageEventArgs e) { e.Graphics.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height); } private void btImprimir_Click(object sender, EventArgs e) { CapturaFormulario(); Impresora.Document = DocumentoParaImprimir; DialogResult Result = Impresora.ShowDialog(); if (Result == DialogResult.OK) { DocumentoParaImprimir.DefaultPageSettings.Landscape = false; DocumentoParaImprimir.Print(); } } private void btVistaPrevia_Click(object sender, EventArgs e) { CapturaFormulario(); VistaPrevia.Document = DocumentoParaImprimir; VistaPrevia.ShowDialog(); } private void CapturaFormulario() { Graphics mygraphics = this.CreateGraphics(); Size sz = this.ClientRectangle.Size; bmp = new Bitmap(sz.Width, sz.Height, mygraphics); Graphics memoryGraphics = Graphics.FromImage(bmp); IntPtr dc1 = mygraphics.GetHdc(); IntPtr dc2 = memoryGraphics.GetHdc(); BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 0, 0, 13369376); mygraphics.ReleaseHdc(dc1); memoryGraphics.ReleaseHdc(dc2); //bmp.Save("prueba.bmp", ImageFormat.Bmp); } [System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern long BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); } }
Este código funciona correctamente pero según las pruebas que he realizado, el resultado de la impresión no es muy nítido y las etiquetas (labels) quedan algo borrosas. Tal vez sea por el contraste de colores o por la resolución del bitmap a 96 píxeles por pulgada, pero si comparas dicha impresión con el texto de un libro o con un documento imprimido desde Word, hay muchísima diferencia. Después de los resultados obtenidos, yo no usaría dicho código en un proyecto donde necesites una buena calidad final.
Para mejorar la nitidez y que se vea correctamente, puedes recurrir a dibujar uno a uno los controles del formulario en el documento a imprimir como se explica a continuación:
2.- Dibujar previamente los controles uno a uno
Como encontré este excelente código: imprimir un formulario usando API, no veo necesario reescribirlo porque es muy completo. El código es muy interesante, está incorporado en un archivo PrintForm.cs y para imprimir un formulario solo debes llamarlo del siguiente modo:
PrintForm pf = new PrintForm(this);
pf.Print();
Puedes realizar, modificando el código, ajustes antialisis o editar propiedades para mejorar la calidad. Personalmente esta segunda opción de dibujar uno a uno los controles es mucho más precisa y nítida. Es similar a cuando tenemos un documento de texto y antes de realizar la impresión, llamamos a e.Graphics.DrawString para dibujar en el documento el texto.
El código, funciona bastante bien y calcula las medidas del texto (o controles) para imprimirlas en otra página si sobrepasan las dimensiones, sin embargo, hay que retocarlo para que no dibuje la barra de título o para que deje elegir la impresora a utilizar (esto no es nada difícil). Pero la idea y el resultado final son excelentes.
Después de analizar y probar el código más detenidamente hay cosas de él que no me gustan. Por ejemplo, los textos son centrados verticalmente "a mano" y solo se pueden alinear a la izquierda. Esto tiene fácil solución usando:
StringFormat FormatoTexto = new StringFormat();
FormatoTexto.Alignment = StringAlignment.Center;
FormatoTexto.LineAlignment = StringAlignment.Center;
Yo recuerdo usar la segunda forma porque la impresión es de muchísima más calidad. Recuerdo también modificar el código para ajustar perfectamente todos los controles, pero esto es hacer pruebas. La primera forma es muy sencilla e incluso se puede optimizar más (según he visto en Microsoft) pero el resultado no tiene buena calidad.
¿Quieres guardar el formulario en un pdf o formato de word?
me sale error en el PixelFormat "que tendriera que agregar un using o que ??"
¿Cuál es el error exacto que te aparece?