el Guille, la Web del Visual Basic, C#, .NET y más...

Imprimir con Visual Basic .NET

 
Publicado el 03/Feb/2009
Actualizado el 03/Feb/2009
Autor: Guillermo 'guille' Som

Ejemplos prácticos de cómo imprimir con Visual Basic .NET (y con un pequeño esfuerzo, cómo hacerlo también en C#)



 

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
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
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


Código de ejemplo (comprimido):

Fichero con el código de ejemplo: ImprimirVB.zip - 54.00 KB

Contiene tres proyectos, para usar con Visual Basic 2008 aunque el código usado en cada formulario y cada clase es válido para cualquier versión de Visual Basic .NET.

(MD5 checksum: FEFE6451FD91BD888ABC7CB9E6812426)


 


La fecha/hora en el servidor es: 13/11/2024 9:46:12

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024