miércoles, 9 de diciembre de 2009

Imprimir el contenido de un DataGridView con PrintDocument (en VB.NET)

Dada la cantidad de comentarios (más de 10 :P) que se produjeron en el post en el que explicaba como imprimir el contenido de un datagrid, me veo en la obligación a hacer el mismo ejemplo en VB.NET, sobre todo porque el código que puse en los comentarios tenía algún error.

Recordando el ejemplo, se trata de un formulario que contiene:
- un datagrid llamado DataGridView1
- un botón llamado Button1
- un PrintDocument llamado PrintDocument1

Y aquí tenéis el código:



Public Class Form1

' Variable a nivel de clase para recordar en qué punto nos hemos quedado
Dim i As Integer = 0

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Fill_DataGrid()
End Sub

Private Sub Fill_DataGrid()

' TODO: rellenar con el código que obtiene los datos de donde sea necesario
' Por ejemplo:

Me.DataGridView1.DataSource = dataSet
Me.DataGridView1.DataMember = "Table"
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.PrintDocument1.Print()
End Sub

Private Sub printDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
' Definimos la fuente que vamos a usar para imprimir
' en este caso Arial de 10
Dim printFont As System.Drawing.Font = New Font("Arial", 10)
Dim topMargin As Double = e.MarginBounds.Top
Dim yPos As Double = 0
Dim linesPerPage As Double = 0
Dim count As Integer = 0
Dim texto As String = ""
Dim row As System.Windows.Forms.DataGridViewRow

' Calculamos el número de líneas que caben en cada página
linesPerPage = e.MarginBounds.Height / printFont.GetHeight(e.Graphics)

' Imprimimos las cabeceras
Dim header As DataGridViewHeaderCell
For Each column As DataGridViewColumn In DataGridView1.Columns
header = column.HeaderCell
texto += vbTab + header.FormattedValue.ToString()
Next

yPos = topMargin + (count * printFont.GetHeight(e.Graphics))
e.Graphics.DrawString(texto, printFont, System.Drawing.Brushes.Black, 10, yPos)
' Dejamos una línea de separación
count += 2

' Recorremos las filas del DataGridView hasta que llegemos
' a las líneas que nos caben en cada página o al final del grid.
While count < linesPerPage AndAlso i < DataGridView1.Rows.Count
row = DataGridView1.Rows(i)
texto = ""
For Each celda As System.Windows.Forms.DataGridViewCell In row.Cells
'Comprobamos que la celda tenga algún valor, en caso de
'permitir añadir filas esto es muy importante
If celda.Value IsNot Nothing Then
texto += vbTab + celda.Value.ToString()
End If
Next

' Calculamos la posición en la que se escribe la línea
yPos = topMargin + (count * printFont.GetHeight(e.Graphics))

' Escribimos la línea con el objeto Graphics
e.Graphics.DrawString(texto, printFont, System.Drawing.Brushes.Black, 10, yPos)
' Incrementamos los contadores
count += 1
i += 1
End While

' Una vez fuera del bucle comprobamos si nos quedan más filas
' por imprimir, si quedan saldrán en la siguente página
If i < DataGridView1.Rows.Count Then
e.HasMorePages = True
Else
' si llegamos al final, se establece HasMorePages a
' false para que se acabe la impresión
e.HasMorePages = False
' Es necesario poner el contador a 0 porque, por ejemplo si se hace
' una impresión desde PrintPreviewDialog, se vuelve disparar este
' evento como si fuese la primera vez, y si i está con el valor de la
' última fila del grid no se imprime nada
i = 0
End If
End Sub

End Class




Happy coding ;)

22 comentarios:

Anónimo dijo...

Saludos eh utilizado tu codigo para intentar imprimir un datagridview de mi form pero no me imprime nada y tambien no me marca ningun error

Pablo Bouzada dijo...

Hola anónimo, como no me des alguna pista más no creo que te pueda ayudar.

Anónimo dijo...

Ok mira extraigo de una base de datos y lleno un datagridview, eh utilizado el codigo para imprimir el datagrid sin embargo manda a imprimir pero no me imprime nada, entra la hoja en la impresora y sale igual en blanco de casualidad sabes a q se debe....Gracias!!!
como te comente no marca ningun error

Pablo Bouzada dijo...

Comprueba que realmente estas mandando texto a la impresora, pon un punto de interrupción en la línea:

While count < linesPerPage AndAlso i < DataGridView1.Rows.Count


Y ejecuta paso a paso a partir de ahí.

Jorge dijo...

Hola, he introducido tu código y me imprimió a la primera. Pero los datos de las diferentes columnas no me coinciden en tabulación con los encabezados. A ver si me explico mejor... los encabezados me aparecen bien tabulados pero los datos me aparecen desordenados y sin espacios entre ellos.

he tratado ver si el problema estaba en la organización de las columnas del datagridview, pero no he conseguido resolver el problema.

Pablo Bouzada dijo...

Hola Jorge,

piensa que la impresión realmente no tiene nada que ver con que haya un datagrid. Los datos se podrían haber cogido de un array, una lista o un datatable. Con lo que se está "escribiendo" en la impresora a mano, con este código:

texto += vbTab + Celda.Value.ToString()

se determina qué se escribe, y con este otro:

yPos = topMargin + (count * printFont.GetHeight(e.Graphics))

' Escribimos la línea con el objeto Graphics
e.Graphics.DrawString(texto, printFont, System.Drawing.Brushes.Black, 10, yPos)

se determina dónde se escribe.

Si no te está tabulando bien los valores, revisa si le tienes que añadir o quitar tabulaciones al primer código.

Anónimo dijo...

saludos tengo el mismo problema de jorge, aparte que tengo un encabezado porque estoy imprimiendo tipo factura unos textbox ke son el enkabezado de la pagina y el datagridview con el codigo ke nos proporcionaste, pero tengo varios problemas aparte de la tabulacion, no me puedo bajar lo que sale en el datagridview siendo que me chocan con el encabezado, tengo un codigo que muestra la vista previa y asi me aparece :/ , y no entendi bien que lo hay que modificar sobre lo de las tabulaciones, a lo que le respondiste a jorge,eso
espero ke me ayudes :( solo es el posiciionamiento del datagridview en la hoja par imprimir me podrias decir como acomodarlo bien que tengo que modificar ?
desde ya muchas gracias

sergio.b dijo...

HOLA, EXCELENTE LA OPCION DE IMPRIMIR EL DATAGRID, TENGO UNA DUDA: SI YO QUISIERA QUE ME GENERE UNA VISTA PREVIA Y LUEGO LA OPCION IMPRIMIR DESDE UNA IMPRESORA QUE EL MISMO USUARIO ESCOJA, COMO LO HARIAS???? ME PUEDES AYUDAR EN ESO??? ES MUY IMPORTANTE Y URGENTE TE LO AGRADEZCO MUCHO, NO SE COMO HACERLO Y EL CODIGO A UTILIZAR SI QUE MENOS GRACIAS

Pablo Bouzada dijo...

Hola sergio.b,

tienes un link en la primera línea de este post que te envía al post original en el que tienes resueltas un par de tus dudas.

Anónimo dijo...

Muchas Gracias, me sirvio un monton, segui asi.

juanito dijo...

hola amigo ... me sale error en esta s lineas de condigo ...

Private Sub Fill_DataGrid()

' TODO: rellenar con el código que obtiene los datos de donde sea necesario
' Por ejemplo:

Me.dgvAutos.DataSource = "DataSet"
Me.dgvAutos.DataMember = "Table"
End Sub

precisamente en esa dos lineas de:

Me.dgvAutos.DataSource = "DataSet"
Me.dgvAutos.DataMember = "Table"

ahi me sale error que el data set no se puede usar como expresion
y en la segunda que no se pude crear una lista secudaria para el campo table ... que hago??????

Pablo Bouzada dijo...

juanito, precisamente lo más importante de esa función es el comentario:
' TODO: rellenar con el código que obtiene los datos de donde sea necesario

que viene a decir que pongas el origen de datos que tengas, no necesariamente se tiene que llamar DataSet y Table, si no que tienes que utilizar el que tengas.
Si no tienes ninguno (o no sabes lo que es) no te molestes en ejecutar el código porque no funcionará.

juanito dijo...

pablo i como ago para saber como rellenar el codigo porfavor ayudame que me urge saber eso :(... como hago para que me funke el codigo????

Pablo Bouzada dijo...

juanito, te recomiendo que empieces por aquí:

http://msdn.microsoft.com/es-es/library/ss7fbaez(v=vs.80).aspx

Cuando te hayas leído los links de esa página tendrás información de sobra para resolver tu duda.

juanito dijo...

amigo yo no utilizo ADO :(.. yo utilizo OleDbConnection :( cry creo que nunca saldre de todas mis dudas para poder imprimir el datagridview :( amigo qu agooooooooooo :( ayudameeee

juanito dijo...

hola hola soy yo de nuevoo.. disculpa que molesto tanto pèro esque tu si sabes de programacion... lo que yo quieor en mi programa es imprimir el datagridview junto con unos texbox(en total 5) los que son los totales de cada columna como ago para imprimir mi datagridview juntos con mis texbox bajo cada columna correspondiente asignada?? ayudame porfavor amigo:(

Unknown dijo...

Hola buenos dias, me salio perfecto el ejemplo, imprime y todo. Es mas modifique el codigo para que te deje elegir la impresora y todo colocando esto en la accion del boton click

If PrintDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
PrintDocument1.PrinterSettings = PrintDialog1.PrinterSettings
PrintDocument1.Print()
End If

Pero ahora tengo un problema como tengo muchas columnas no entra en la pagina. Como hago para poner la hoja en horizonal asi entran todas las columnas?

Desde ya muchas gracias

Pablo Bouzada dijo...

Prueba con esto: http://msdn.microsoft.com/es-es/library/system.windows.forms.pagesetupdialog(VS.80).aspx

lowellpelikno dijo...

HOLA AMIGO MUY BUENO TU EJEMPLO PERO YO TENGO UN ERROR EN LA SIGUIENTE LINEA

texto += celda.Value.ToString();

Referencia a objeto no establecida como instancia de un objeto.
APRECIARÍA MUCHO TU AYUDA ASÍ COMO TU PRONTA RESPUESTA

Pablo Bouzada dijo...

@lowellpelikno, ¿qué tal si compruebas si celda== null o celda.Value == null?

edgar dijo...

Hola Pablo de antemano gracias por tu aporte (muy bueno) pero sabs tengo cerca de 400 lineas en el DataGridView y cuando imprime se mandan a imprimir infinidad de hojas y en todas se repite la información. Según yo deben ser aproximadamente 7 hojas, pero no se que pasa que se imprimen sin parar y todas con los mismos renglones :( Saludos y gracias nuevamente!!!

Pablo Bouzada dijo...

@edgar: el código tiene que hacer lo que tú quieras y no al revés. Comenta la línea que "escribe" en la impresora: e.Graphics.DrawString(...) pon un punto de interrupción donde creas que no se está haciendo lo que tú quieres y listo :)