La batallita del agüelo y otros copyrights:
Este artículo fue originalmente publicado en Todo Programación número 11
(Febrero 2005) y se prohíbe la reproducción total o parcial sin el
consentimiento escrito del autor.
Este mismo artículo lo autoricé para su publicación exclusiva en la revista
electrónica de Ineta Latam, lo aviso para que nadie venga después diciendo
algo así como "Guille, esto mismo lo he visto publicado en tal revista en
formato PDF".
Otra cosa que quiero aclarar es que esto lo escribí en Enero de 2005 (o
incluso antes), por tanto, la versión de Visual Basic .NET usada para los
ejemplos y esas cosas, es la conocida como 2003, esto lo digo por si ahora
hay otras formas más fáciles de imprimir, que puede que las haya, pero que
este que escribe ni lo ha comprobado, más que nada porque como no me dedico
a hacer programas y los que hago son para mi uso particular y esos no
necesitan imprimir nada, pues... en fin...
Ya no me enrollo más, aquí tienes el artículo: Imprimir con Visual Basic .NET
Introducción
Muchos de los usuarios de Visual Basic 6 que han migrado a .NET han
sufrido (incluso están sufriendo) la carencia de este "nuevo" entorno de
programación de un objeto que les permita imprimir de forma tan fácil como
lo era el objeto Printer o de una colección que les permita saber las
impresoras disponibles en el sistema. En este artículo explicaremos cómo
imprimir en .NET y comprobaremos que aunque parezca complicado, a la larga,
imprimir en .NET es igual o más fácil que con VB6, aunque tratándose de un
lenguaje orientado a objetos, debemos cambiar un poco el chip.
Nota:
Actualmente el equipo de desarrollo de Visual Basic está sacando una serie
de paquetes de ayuda al programador llamados genéricamente Visual Basic 2005
Power Packs, entre ellos está uno para facilitar la impresión: PrintForm
component, el cual se puede descargar desde esta dirección:
http://msdn2.microsoft.com/en-us/vbasic/aa701261.aspx.
Los precedentes
Lo primero que debemos tener en cuenta es que todo lo que queramos hacer
en .NET debemos hacerlo usando las clases que este entorno nos ofrece y el
tema de imprimir no es una excepción. En Visual Basic 6 solamente
disponíamos del objeto Printer, que era el que realmente nos permitía
imprimir, de la colección Printers, la cual podíamos usar para saber las
impresoras que teníamos disponibles, además del control para diálogos
comunes, el cual nos permitía seleccionar y configurar la impresora que
queríamos usar, además de permitirnos indicar el número de páginas a
imprimir, etc., aunque no siempre funcionara como a nosotros nos hubiera
gustado y en ocasiones tuviésemos que acudir a las funciones del API de
Windows si realmente queríamos hacer algo verdaderamente práctico. En .NET
ha cambiado un poco la forma de hacer todo lo que hacíamos en VB6, ya no
existen "objetos" que nos permitan imprimir ni colecciones que nos permitan
saber las impresoras disponibles, bueno, un poco sí, pero como veremos de
una forma más ordenada y algo diferente. Esa diferencia es la que a muchos
les ha causado desesperación e incluso les ha llevado a un consumo excesivo
de Aspirinas. En los siguientes puntos veremos que nuestra vida puede ser
más soportable e incluso más fácil si aprendemos a manejar las clases de
.NET que nos permiten imprimir y configurar las opciones de impresión.
La solución: Cómo imprimir en .NET
Básicamente para imprimir en .NET solamente necesitamos una
clase: PrintDocument la cual está definida en el espacio de
nombres System.Drawing.Printing. Con esta clase tenemos todo lo
necesario para imprimir (en la impresora predeterminada)
cualquier tipo de documento, para ello simplemente necesitamos
llamar al método Print y asunto arreglado. Seguramente el lector
se preguntará que si esto es así, ¿dónde está el problema?
Problema, lo que se dice problema, realmente no hay ninguno, lo
que ocurre es que esta clase, particularmente el método Print,
se utiliza sin tener que indicar qué es lo que queremos
imprimir, y aquí es donde podría estar ese problema. El método
Print de la clase PrintDocument lo que realmente hace es
"despertar a la bestia", es decir, dar las instrucciones
pertinentes al motor de .NET para que se inicie el proceso de
impresión, dicho proceso se lleva a cabo básicamente utilizando
el evento PrintPage de la clase PrintDocument, en este evento es
donde tendremos que hacer todo lo necesario para que se imprima
lo que queramos imprimir. Por tanto, para poder controlar lo que
se va a imprimir, debemos escribir todo nuestro código en ese
evento, el cual se produce para cada página que deba imprimirse;
posiblemente este sea el punto problemático, al menos desde el
punto de vista del programador de VB6, ya que antes para
controlar lo que debía imprimirse simplemente usábamos el método
Print del objeto Printer, (o de cualquier objeto del tipo
Printer que hubiésemos obtenido de la colección Printers), al
que le indicábamos qué es lo que debía imprimirse, aunque en VB6
éramos nosotros los que debíamos comprobar cuando cambiar de
página, cuando imprimir la cabecera, etc. En .NET todo esto se
hace en un solo sitio: el evento PrintPage, por tanto podemos
decir que en .NET es más fácil imprimir y sobre todo controlar
cómo y dónde se imprime cada cosa, ya que, si lo simplificamos
al máximo, todo se hace en un solo sitio: el evento PrintPage de
la clase PrintDocument.
Para muestra, un botón
En el listado 1 podemos ver un ejemplo de la forma más simple
de imprimir en .NET, en este ejemplo mostramos la cadena "Hola,
Mundo" en la parte superior de la página usando una fuente Arial
de 24 puntos en negrita.
Private Sub btnImprimirSimple_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles btnImprimirSimple.Click
' ejemplo simple para imprimir en .NET
' Usamos una clase del tipo PrintDocument
Dim printDoc As New PrintDocument
' asignamos el método de evento para cada página a imprimir
AddHandler printDoc.PrintPage, AddressOf print_PrintPage
' indicamos que queremos imprimir
printDoc.Print()
End Sub
Private Sub print_PrintPage(ByVal sender As Object, _
ByVal e As PrintPageEventArgs)
' Este evento se producirá cada vez que se imprima una nueva página
' imprimir HOLA MUNDO en Arial tamaño 24 y negrita
' imprimimos la cadena en el margen izquierdo
Dim xPos As Single = e.MarginBounds.Left
' La fuente a usar
Dim prFont As New Font("Arial", 24, FontStyle.Bold)
' la posición superior
Dim yPos As Single = prFont.GetHeight(e.Graphics)
' imprimimos la cadena
e.Graphics.DrawString("Hola, Mundo", prFont, Brushes.Black, xPos, yPos)
' indicamos que ya no hay nada más que imprimir
' (el valor predeterminado de esta propiedad es False)
e.HasMorePages = False
End Sub
Listado 1. Ejemplo simple de impresión
Como podemos comprobar el proceso es bien simple:
- Primero tenemos el código usado para dar la orden de
imprimir, (este código puede estar en el evento Click de un
botón), en el que creamos un nuevo objeto del tipo
PrintDocument al que asignamos el procedimiento de evento a
usar cuando se vaya a imprimir cada página, cosa que
ocurrirá después de llamar al método Print de esta clase.
- En el evento PrintPage le indicamos a .NET que es lo que
queremos imprimir, en nuestro caso sólo la cadena "Hola,
Mundo", para imprimir dicho texto utilizamos el método
DrawString del objeto Graphics, (una propiedad del segundo
parámetro pasado al evento).
Esta simpleza es en parte porque tampoco hacemos demasiadas
cosas en el código del evento PrintPage, aunque aquí mostramos
algunas de las cosas necesarias, como por ejemplo indicar el
tipo de fuente a usar para la impresión, la cual se puede
cambiar en cada una de las líneas a imprimir (e incluso en
distintas partes de esa línea), ya que cada línea se "dibuja"
por medio del método DrawString del objeto Graphics obtenido del
segundo parámetro pasado a este método. Como vemos en DrawString
debemos indicarle que es lo que queremos imprimir, con qué tipo
de fuente y en qué posición. Por último indicaremos si quedan
más páginas a imprimir, esto lo haremos asignado un valor
verdadero o falso a la propiedad HasMorePages del objeto
recibido como segundo parámetro del evento, si el valor asignado
es True estaremos indicando que queremos seguir imprimiendo, por
el contrario, asignando un valor False terminaremos la
impresión, cosa que también ocurrirá si no le asignamos
expresamente nada a esa propiedad.
Configurar la impresión
Pero para ser claros esta no será la forma "habitual" de usar
este evento, ya que en la mayoría de los casos imprimiremos más
de una página y seguramente querremos asignar otros valores a la
impresora, como los márgenes, el número de copias, el tamaño del
papel, la orientación de la impresión (vertical o apaisada) e
incluso, lo más importante, qué impresora usar. Todos estos
valores los podremos indicar usando un objeto del tipo
PrinterSettings, el cual, entre otras cosas, nos permite saber
cuáles son las impresoras que tenemos disponibles en el equipo
actual. Por tanto, lo habitual será que usemos un objeto de esta
clase en conjunto con el de la clase principal para imprimir:
PrintDocument.
Seleccionar la impresora a usar
Otra de las tareas que seguramente nos veremos en la
necesidad de ofrecer a los usuarios de nuestras aplicaciones es
que puedan seleccionar de una manera fácil qué impresora usar y
darles la posibilidad de que ajusten sus preferencias de
impresión. Todo esto lo podríamos hacer manualmente creando un
formulario que utilice un objeto del tipo PrinterSettings y que
le ofrezca a los usuarios las opciones que creamos conveniente,
pero para que todo sea, digamos, más homogéneo y no tengamos que
reinventar la rueda, podemos hacer que toda esta configuración y
selección de la impresora a usar la ofrezcamos mediante los
mismos cuadros de diálogo que utiliza el resto de aplicaciones,
los cuales se basan en los cuadros de diálogos comunes del
sistema operativo, si esta es nuestra elección, nos resultará
fácil, ya que podemos usar la clase PrintDialog. Con un objeto
de esta clase, tal como hemos comentado, podemos mostrar el
mismo cuadro de diálogo que utilizan el resto de aplicaciones de
Windows, con la ventaja añadida de que podemos configurar las
opciones que se mostrarán, de forma que lo configuremos para que
se adapte, en la medida de lo posible, a las preferencias de
nuestra aplicación.
En breve veremos cuáles son las opciones que podemos usar para
ajustar este cuadro de diálogo a nuestras preferencias o a la de
nuestra aplicación.
Las clases para imprimir en .NET
Como hemos comentado en los párrafos anteriores, existen
varias clases que nos permitirán tanto imprimir como configurar
la impresión. Todo esto es posible gracias a las clases que .NET
Framework pone a nuestra disposición para poder realizar esta
tarea. Echando mano del comodín que los que nos dedicamos a
escribir artículos o libros, tenemos que decir que en este
artículo no disponemos del espacio suficiente para enumerar en
detalle todas las clases, delegados y enumeraciones que tanto el
espacio de nombres System.Drawing.Printing como
System.Windows.Forms pone a nuestra disposición para realizar
tareas relacionadas con la impresión, por tanto haremos una
pequeña criba para mostrar solamente los tipos que desde nuestro
punto de vista pueden resultarnos más útiles, particularmente
porque serán los que utilicemos en la mayoría de los casos.
La clase PrintDocument
Esta es la clase elemental o principal si queremos imprimir
en .NET, como hemos comentado anteriormente, de los miembros que
esta clase ofrece principalmente usaremos tres:
- El método Print es el que iniciará el proceso de
impresión, haciendo que se produzcan los eventos necesarios
para controlar lo que queremos imprimir.
- El evento PrintPage, el cual se producirá cada vez que
tengamos que imprimir una página. Dentro de este evento es
donde haremos todo lo que tengamos que hacer para imprimir
cada una de las páginas, desde aquí podemos controlar si
quedan más páginas por imprimir, etc.
- La propiedad PrinterSettings a la que podemos asignarle
un objeto del mismo nombre en el que indicamos la impresora
a usar y otros valores como el rango de página, el número de
copias, el tamaño del papel, la orientación, etc.
Del resto de miembros de esta clase podemos destacar la
propiedad DocumentName a la que podemos asignar el nombre del
documento que estamos imprimiendo y que será el que se muestre
en el estado de la impresión. También tenemos el evento
QueryPageSettings el cual se produce justo antes del evento
PrintPage y que nos permite asignar valores "particulares" a
cada una de las páginas a imprimir antes de que se produzca el
evento PrintPage.
La clase PrinterSettings
Tal y como hemos estado indicando en los párrafos anteriores, esta clase
nos permite especificar características de impresión como la impresora a
usar, el número de copias a imprimir, el rango de páginas, etc. Realmente no
necesitamos indicar nada, al menos de forma explícita para usar un objeto de
esta clase, ya que al crear una nueva instancia los valores predeterminados
serán los que tengamos asignado en la impresora, valga la redundancia,
predeterminada de nuestro sistema. Pero si queremos modificar esos valores,
tendremos que asignarlos a las propiedades de esta clase, entre las que
podemos destacar las siguientes:
- Copies indica el número de copias a imprimir.
- FromPage indica la página a partir de la que queremos imprimir.
- MaximunPage indica el número máximo de copias que se pueden indicar.
- MinimunPage es el número mínimo de copias que podemos indicar.
- ToPage indica la página hasta la que queremos imprimir.
Tanto MaximunPage como MinimunPage se utilizarán para indicar los valores
máximo y mínimo de las páginas disponibles y se usarán con una clase del
tipo PrintDialog.
Del resto de propiedades también podemos destacar las colecciones:
- InstalledPrinters es una propiedad compartida y por tanto podemos
usarla sin necesidad de crear una instancia de la clase PrinterSettings,
esta colección devuelve un array de tipo String con el nombre de cada
una de las impresoras instaladas en el sistema.
- PaperSizes es una colección con elementos del tipo PaperSize que nos
permite saber los tamaños de papel que la impresora soporta. Cada
elemento del tipo PaperSize nos proporciona información sobre el tamaño
(ancho y alto), el nombre y la "clase" de papel, que no es ni más que
una enumeración del tipo PaperKind.
- PrinterResolutions es una colección con elementos del tipo
PrinterResolution, de forma que podamos averiguar las resoluciones (y
calidades) permitidas por la impresora. Cada uno de estos elementos nos
indicará tanto la resolución horizontal como la vertical, además de la
calidad de impresión, especificada con uno de los valores de la
enumeración PrinterResolutionKind, cuyos valores pueden ser: Custom,
Draft, High, Low y Medium.
De los métodos, posiblemente el que más nos puede interesar es:
- CreateMeasurementGraphics el cual nos permite conseguir un objeto
del tipo Graphics con el cual podemos averiguar ciertas características
de la impresora sin necesidad de tener que imprimir, ya que el objeto
devuelto por este método es igual al que se incluye en la clase
PrintPageEventArgs, usada como segundo parámetro en los eventos
producidos por la clase PrintDocument.
Como regla general deberíamos tener una variable del tipo PrinterSettings
para usarla como almacenamiento de las preferencias de impresión de nuestros
usuarios, ya que esta clase se utiliza tanto con PrintDocument como con
PrintDialog.
La clase PrintDialog
Esta clase nos servirá para que nuestros usuarios seleccionen la
impresora a usar así como para que indiquen ciertas características
relacionadas con la impresión, como la calidad del papel, el número de
copias, etc., con la ventaja de que todo esto lo haremos usando el mismo
cuadro de diálogo común incluido en Windows y que es el que la práctica
totalidad de aplicaciones de este sistema operativo utilizarán, tal como
podemos comprobar en la figura 1.
Figura 1. El cuadro de diálogo para seleccionar la impresora
En la figura 1 podemos comprobar que hay ciertos elementos que puede que
no nos interese mostrar o dejar habilitados, por ejemplo, si nuestra
aplicación no permite imprimir el código seleccionado no tiene mucho
significado ofrecerle la opción "Selección", (la figura está capturada de un
Windows XP en inglés, por eso se muestran las opciones en inglés), lo mismo
ocurre con el botón de ayuda o con las cajas de texto que permiten indicar
el rango de páginas a imprimir. Todas estas características las podemos
habilitar o deshabilitar mediante algunas de las propiedades de la clase
PrintDialog, tales como: AllowPrintToFile, AllowSelection, AllowSomePages,
PrintToFile, ShowHelp y ShowNetwork.
La ayuda del cuadro de diálogo de imprimir
Como curiosidad, decir que la propiedad ShowHelp nos permite indicar si se
debe mostrar o no el botón de ayuda, (no la interrogación de la barra de
títulos que siempre estará funcional), pero entre las propiedades de esta
clase no tenemos ninguna a la que indicarle que ayuda se debe mostrar si
pulsamos en ese botón, en lugar de eso, de lo que disponemos es de un
evento: HelpRequest el cual se producirá cuando el usuario pulse en dicho
botón, por tanto si queremos mostrar algún tipo de ayuda, tendremos que usar
ese evento para mostrar la información que creamos conveniente para nuestro
cuadro de diálogo.
Al igual que en el resto de cuadros de diálogos de .NET,
tenemos el método ShowDialog que será el que nos permita mostrar
el cuadro de diálogo y saber si el usuario ha pulsado en el
botón OK (Aceptar) o Cancelar, para que de esta forma sepamos si
debemos seguir con el proceso de impresión o no.
La propiedad PrinterSettings será la que nos permita saber lo
que el usuario ha seleccionado, además de ser la que usemos para
asignar los valores predeterminados o bien los que tengamos
almacenado de ocasiones anteriores; tal como indicamos
anteriormente, lo habitual será que tengamos una variable del
tipo de esta propiedad con las preferencias del usuario, por
tanto antes de llamar al método ShowDialog deberíamos asignar a
esta propiedad la variable que tengamos con las preferencias del
usuario y si no se ha cancelado, debemos asignar nuevamente el
resultado de dichas preferencias, tal como mostramos en el
listado 2.
''' <summary>
''' Seleccionar la impresora.
''' </summary>
''' <returns>
''' Devuelve True si todo fue bien o false si se canceló
''' </returns>
Private Function seleccionarImpresora() As Boolean
Dim prtDialog As New PrintDialog
If prtSettings Is Nothing Then
prtSettings = New PrinterSettings
End If
With prtDialog
.AllowPrintToFile = False
.AllowSelection = False
.AllowSomePages = False
.PrintToFile = False
.ShowHelp = False
.ShowNetwork = True
.PrinterSettings = prtSettings
If .ShowDialog() = DialogResult.OK Then
prtSettings = .PrinterSettings
Else
Return False
End If
End With
Return True
End Function
Listado 2.
La clase PrintPreviewDialog
Esta clase nos permitirá mostrar una ventana con la vista preliminar del
documento que queremos imprimir, de forma que los usuarios de nuestra
aplicación pueden ver lo que se imprimirá. Debido a que esta clase al estar
derivada de Form tiene todas las propiedades, métodos y eventos de cualquier
formulario además de los relacionados con la previsualización del documento
a imprimir, veamos solamente los dos miembros que nos interesarán más:
- El método ShowDialog será el que se encargue de mostrar el
formulario con la vista preliminar.
- A la propiedad Document le asignaremos un objeto del tipo
PrintDocument que será el que utilicemos para saber qué es lo que
queremos imprimir.
NOTA:
Por regla general deberíamos asignar a la propiedad Document de la clase
PrintPreviewDialog el mismo objeto PrintDocument usado para imprimir, ya que
la clase PrintPreviewDialog se encargará de que se produzcan los mismos
eventos que si hubiésemos llamado al método Print del objeto PrintDocument
asignado, de forma que lo que se muestre mediante este diálogo sea lo mismo
que se imprima, que es al fin y al cabo lo que queremos conseguir.
Tal como hemos resaltado en la nota, tanto el método Print de la clase
PrintDocument como la clase PrintPreviewDialog utilizan los mismos eventos
del objeto PrintDocument, por tanto podríamos usar un método genérico que
sea el encargado de mostrar una vista preliminar de lo que queremos imprimir
o de mandarlo a la impresora, de esta forma podemos usar de forma común las
opciones ofrecidas al usuario, como por ejemplo permitir la selección de la
impresora antes de imprimir, etc.
En el listado 3 podemos ver cómo podría ser ese método genérico para
elegir entre imprimir o previsualizar lo que deseamos imprimir.
Private Sub imprimir(ByVal esPreview As Boolean)
' imprimir o mostrar el PrintPreview
If prtSettings Is Nothing Then
prtSettings = New PrinterSettings
End If
If chkSelAntes.Checked Then
If seleccionarImpresora() = False Then Return
End If
If prtDoc Is Nothing Then
prtDoc = New System.Drawing.Printing.PrintDocument
AddHandler prtDoc.PrintPage, AddressOf prt_PrintPage
End If
' la línea actual
lineaActual = 0
' la configuración a usar en la impresión
prtDoc.PrinterSettings = prtSettings
If esPreview Then
Dim prtPrev As New PrintPreviewDialog
prtPrev.Document = prtDoc
prtPrev.Text = "Previsualizar documento"
prtPrev.ShowDialog()
Else
prtDoc.Print()
End If
End Sub
Listado 3.
Tal como podemos ver en la figura 2, el formulario (o cuadro
de diálogo) de previsualización nos permite seleccionar el
número de página a mostrar, si queremos ver una o más páginas a
un mismo tiempo, el porcentaje de ampliación e incluso imprimir
lo que estamos viendo, todos estas características ya están
incluidas en ese formulario.
Figura 2. El formulario de previsualización
La clase PrintPreviewControl
Si nuestra intención es crear nuestro propio formulario de
previsualización, también podemos hacerlo si usamos el control
PrintPreviewControl que es el que la clase PrintPreviewDialog
utiliza, si bien todos los botones y opciones tendremos que
crearlos nosotros, para ello podemos usar los miembros
específicos de este control, tales como:
- AutoZoom lo usaremos para que al cambiar el tamaño del
control se cambie también la página mostrada.
- Columns indica el número de páginas a mostrar cuando se
elija la orientación horizontal (apaisada).
- Document es donde asignaremos el objeto PrintDocument a
imprimir.
- Rows indica el número de páginas a mostrar cuando
elijamos la orientación vertical.
- Zoom para indicar la ampliación con la que queremos
mostrar los documentos.
- StartPageChanged en un evento que se producirá cada vez
que cambiemos la página de inicio (este evento nos servirá
para crear un equivalente al NumericDropDown usado en la
clase PrintPreviewDialog).
Si también quisiéramos implementar un botón para imprimir,
tendremos que manejar nosotros mismos la impresión, pero
realmente resultaría fácil, ya que lo único que tendríamos que
hacer es llamar al método Print del objeto PrintDocument
asignado a la propiedad Document.
Como siempre la última palabra la tendremos nosotros y
dependiendo de lo que queramos hacer usaremos una clase u otra.
Conclusión: Imprimir en .NET es fácil
Tal como hemos podido comprobar, imprimir en .NET no es tan
complicado como en un principio pudiera parecer, además de que
tenemos valores añadidos que nos permiten un mayor control sobre
la impresión y, especialmente, sobre las opciones que podemos
ofrecer a los usuarios de nuestras aplicaciones, ya que como
hemos visto, hacer algo como la presentación preliminar, que en
otros lenguajes nos obligaría a escribir bastante código, es tan
sencillo como crear un nuevo objeto en la memoria y asignar un
par de propiedades.
Por supuesto, lo que no es totalmente sencillo ni automático es
la presentación o impresión de los datos que necesitamos
mostrar, al menos si queremos darle un toque, digamos,
profesional a nuestra impresión, ya que seremos nosotros los que
debamos "afinar" en la forma de mostrar esos resultados; pero de
todas formas eso es algo que siempre nos tocará hacer,
utilicemos el entorno de programación que utilicemos, la ventaja
de usar lo que .NET nos ofrece es que tenemos ciertas
posibilidades de hacerlo de una forma más o menos fácil y que de
alguna forma nos facilita bastante la tarea, aunque para la
mayoría de situaciones será más que suficiente e incluso podemos
automatizar, principalmente porque podemos aprovechar las
posibilidades que nos da la programación orientada a objetos de
crear nuestras propias clases e implementar métodos de impresión
que se adapten a los datos que tengamos que mostrar, ya que una
de las claves de la POO es la abstracción y que mejor forma de
abstracción que crear un método que se encargue de manejar los
datos que la propia clase mantiene y que "sepa" cómo mostrar
esos datos e incluso qué formato deben aplicarse a la hora de
presentar esos datos por la impresora. Posiblemente tengamos que
quemar unas cuantas neuronas más a la hora de "concebir" esa
clase, pero a la larga ese esfuerzo habrá valido la pena ya que
puede suponernos una forma de "olvidarnos" de esos pequeños
detalles de ajustar cada una de las líneas que vamos a imprimir.
De seguro que el lector puede pensar que tampoco es necesario
ese "esfuerzo" extra para imprimir una línea, y seguramente
tenga razón, pero precisamente si cuando diseñamos nuestras
clases para manejar datos tenemos en cuenta estos detalles,
seguramente nos resultará más fácil realizar cambios en
ocasiones futuras. Por ejemplo, si tenemos una clase en la que
hay datos de cadena y de tipo numérico y queremos mostrar esos
datos, la forma de tratar cada uno de esos datos será diferente,
ya que con toda seguridad los datos numéricos querremos
ajustarlos a la derecha y los de cadena a la izquierda, además
de que si debemos recortar la información que tenemos que
mostrar, con total seguridad preferiremos "truncar" los datos de
cadena antes que los numéricos. En este tipo de situaciones si
dejamos que el código de la clase sea el que decida estos
truncamientos, (realmente el código de la clase no va a decidir
nada, ya que tendremos que ser nosotros los que hagamos esa
decisión), siempre será preferible que tener que hacerlo en el
propio evento de impresión, sobre todo si ese mismo evento es el
que usaremos para imprimir datos de diferentes tipos, que pueden
proceder de diferentes clases, pero si las clases usadas para
contener los datos están "preparadas" para imprimir el contenido
de cada línea, entonces resultará tan sencillo como llamar a un
método de cada una de esas clases...
En el listado 4 podemos ver cómo quedaría el evento PrintPage si
hiciéramos algo de lo que acabamos de comentar.
' El evento usado mientras se imprime el documento
Private Sub prt_PrintPage(ByVal sender As Object, _
ByVal e As PrintPageEventArgs)
' Este evento se produce cada vez que se va a imprimir una página
Dim lineHeight As Single
Dim yPos As Single = e.MarginBounds.Top
Dim leftMargin As Single = e.MarginBounds.Left
Dim printFont As System.Drawing.Font
' Asignar el tipo de letra
printFont = prtFont
lineHeight = printFont.GetHeight(e.Graphics)
Dim fontTitulo As New Font("Arial", 20, FontStyle.Bold)
e.Graphics.DrawString("Listado de " & Título, fontTitulo, _
Brushes.Black, leftMargin, yPos)
yPos += fontTitulo.GetHeight(e.Graphics)
' imprimir la cabecera de la página
yPos = Datos(0).CabeceraImpresión(e, printFont, yPos)
' imprimir cada una de las líneas de esta página
Do
yPos += lineHeight
e.Graphics.DrawString(Datos(lineaActual).LineaImpresión, _
printFont, Brushes.Black, leftMargin, yPos)
lineaActual += 1
Loop Until yPos >= e.MarginBounds.Bottom _
OrElse lineaActual >= Datos.Count
If lineaActual < Datos.Count Then
e.HasMorePages = True
Else
e.HasMorePages = False
End If
End Sub
Listado 4.
En el código completo que acompaña a este artículo, hay un proyecto en el
que he definido una interfaz con cuatro métodos, una colección para
almacenar objetos del tipo de la interfaz, una clase abstracta que define
los dos métodos usados en el evento mostrado en el Listado 4, tres clases
diferentes para ver cómo poder usar tanto la case abstracta como la
interfaz, dos de ellas se basan en la clase abstracta (por tanto parte del
código ya estará disponible) y la tercera simplemente implementa la interfaz
y define los cuatro métodos; también incluyo un formulario el cual podemos
utilizar de forma genérica con cualquiera de esas tres clases o de cualquier
otra que utilice la interfaz o la clase abstracta. De esta forma podrás
comprobar que no todo son "palabras".
Lo vamos a dejar aquí, porque aunque sería interesante explicar todo el
código con más detalle, eso me llevaría un montón de horas y no es plan de
abusar ;-)))
De todas formas, confío que con lo aquí explicado más el código incluido en
el ZIP sea más que suficiente para comprender cómo podemos imprimir en .NET
Framework con Visual Basic para .NET.
¡Feliz impresión!
Nos vemos.
Guillermo
Espacios de nombres usados en el código de este artículo:
System.Drawing.Printing