Os Microsoft Reports estão normalmente associados ao controlo Microsoft ReportViewer, e normalmente quem os usa, usa normalmente este controlo. É verdade que este controlo permite uma série de funcionalidades muito importantes e simples de utilizar, como pré-visualizar, exportar e imprimir, mas nem sempre nas nossas aplicações, é necessário pré-visualizar para imprimir.
Os relatórios *.rdlc não têm uma função built-in que permita, de uma forma directa, a impressão de um relatório. Para o fazer, é necessário fazer um render de um relatório para uma Stream, e depois, usando um PrintDocument, colocar a Stream respectiva, que se encontra numa lista de Streams, na folha que está a imprimir.
Uma aplicação simples do método anteriormente descrito, é usando uma classe semelhante a esta:
Imports System.IO
Imports System.Data
Imports System.Text
Imports System.Drawing.Imaging
Imports System.Drawing.Printing
Imports System.Collections.Generic
Imports Microsoft.Reporting.WinForms
Public Class ReportUtils
Implements IDisposable
Private currentPageIndex As Integer
Private tmpFileName As String = String.Empty
Private streamList As List(Of Stream)
'''<summary>
''' Adiciona a Stream à lista de Streams
'''</summary>
Private Function CreateStream(ByVal name As String, _
ByVal fileNameExtension As String, _
ByVal encoding As Encoding, _
ByVal mimeType As String, _
ByVal willSeek As Boolean) As Stream
tmpFileName = My.Computer.FileSystem.GetTempFileName()
Dim s As New FileStream(tmpFileName, FileMode.Create)
streamList.Add(s)
Return s
End Function
'''<summary>
''' Exporta o ficheiro para uma lista de Streams
'''</summary>
Private Sub ExportToStream(ByVal report As LocalReport)
Dim deviceInfo As New StringBuilder
With deviceInfo
.Append("<DeviceInfo>")
.Append(" <OutputFormat>EMF</OutputFormat>")
.Append(" <PageWidth>8.5in</PageWidth>")
.Append(" <PageHeight>11in</PageHeight>")
.Append(" <MarginTop>0.25in</MarginTop>")
.Append(" <MarginLeft>0.25in</MarginLeft>")
.Append(" <MarginRight>0.25in</MarginRight>")
.Append(" <MarginBottom>0.25in</MarginBottom>")
.Append("</DeviceInfo>")
End With
Dim warnings() As Warning = Nothing
report.Render("Image", deviceInfo.ToString, _
AddressOf CreateStream, warnings)
For Each s As Stream In streamList
s.Position = 0
Next
deviceInfo = Nothing
End Sub
'''<summary>
''' Quando o PrintDocument está a imprimir, desenha
''' a página correspondente, da lista de Streams
'''</summary>
Private Sub PrintPage(ByVal sender As Object, _
ByVal ev As PrintPageEventArgs)
Using pageImage As New Metafile(streamList(currentPageIndex))
currentPageIndex += 1
ev.Graphics.DrawImage(pageImage, ev.PageBounds)
ev.HasMorePages = (currentPageIndex < streamList.Count)
End Using
End Sub
'''<summary>
''' Imprime um relatório sem visualização
'''</summary>
'''<param name="report">Relatório a imprimir</param>
Public Sub Print(ByVal report As LocalReport)
streamList = New List(Of Stream)
' Exporta o ficheiro para uma lista de Streams
Call ExportToStream(report)
If streamList IsNot Nothing AndAlso streamList.Count > 0 Then
' Inicia o processo de impressão
Using printDoc As New PrintDocument()
If Not printDoc.PrinterSettings.IsValid Then
Dim msg As String = "Impressora não disponível ou não válida!"
Throw New ArgumentException(msg)
End If
AddHandler printDoc.PrintPage, AddressOf PrintPage
printDoc.Print()
End Using
End If
End Sub
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Try
' Fecha as streams e apaga a lista
If streamList IsNot Nothing Then
For Each s As Stream In streamList
s.Close()
Next
streamList.Clear()
streamList = Nothing
End If
' Apaga o ficheiro temporário (caso exista)
If tmpFileName <> String.Empty AndAlso _
IO.File.Exists(tmpFileName) Then
IO.File.Delete(tmpFileName)
End If
tmpFileName = String.Empty
Catch ex As Exception : End Try
End Sub
End Class
Depois, para imprimir o relatório, é apenas necessário:
Imports Microsoft.Reporting.WinForms
Public Class frmMain
'''<summary>
''' Imprime o relatório
'''</summary>
Private Sub btnPrint_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnPrint.Click
Try
Dim rpt As New LocalReport
rpt.ReportPath = Application.StartupPath & "\..\..\rptProducts.rdlc"
' ---------------------------------------------------------
' Definir DataSource, Parameters, etc para o relatório
' ---------------------------------------------------------
Using cls As New ReportUtils
cls.Print(rpt)
End Using
Catch ex As Exception
MessageBox.Show(ex.Message, My.Application.Info.Title, _
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
End Class
Neste caso utiliza-se o bloco Using ... End Using, uma vez que a classe implementa um método IDisposable. Para mais informação sobre este bloco e a sua utilização, podem ver o seguinte artigo: VB.NET: AndAlso, OrElse e Using
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
14 comentários:
Para mim sempre dá erro nesta linha:
report.Render("Image", deviceInfo.ToString, AddressOf CreateStream, warnings)
A mensagem do erro é:
LocalProcessingException was unhandled.
An error occured during local report processing.
Se puderes me ajudar, ficaria muito grato.
Olá,
Verifica qual a mensagem que tens no inner exception quando o erro ocorre.
Bom Dia amigo, muito bom o Post, funcionou perfeitamente, Obrigado.
Ola, implementei seu codigo no meu projeto, mas ao execua-lo tambem estou recebendo o erro "An error occured during local report processing". Minha duvida é se apesar do DataSource estar definido dentro do Report eu tenho que defini-lo dentro do meu codigo tambem ?. Se sim você poderia dar um exemplo de como faço essa definição ?.
OBrigado,
Eduardo
Olá Eduardo,
Sim claro, é preciso definir a datasource do relatório.
Vê este artigo que mostra como o fazer: VB.NET: Microsoft Reporting Services
Ola Jorge, obrigado pelo artigo que descreve como definir o DataSource, muito bom.
Fiz a definição do DataSource mas ao executar o código estou recebendo a menssagem "An error occured during local report processing". Colocando um Debug descobri que o erro esta ocorrendo na Sub "Public Overloads Sub" logo após o comando "tmpFilename = String.Empty". Coloquei dentro do Catch o comando "MessageBox.Show(ex.InnerException.ToString)" mas ele não mostra nada.
Você tem alguma sugestão do que fazer para descobrir este erro ?.
Grato,
Eduardo
Fantastico.
funcionou perfeitamente. Cheguei até a alterar um pouco o código com a hipótese de alterar a impressora em que desejo imprimir
Jorge Paulino, bom dia!
Estou com o mesmo problema do Eduardo. Ao executar vem a mensagem:
"An error occured during local report processing"
Já segui as dicas acima porém, sem sucesso.
Qual seria o erro?
Agradeço pela ajuda.
Giovani
Olá Giovani,
Qual é o erro que dá na inner exception?
Em vez de ex.Message usar ex.InnerException
Ola Jorge, esta dando erro em LocalReprt e tbm em Warning.
Podes me ajudar????
cara, aqui no meu projeto funcionou em alguma maquinas e outras não, dentro do mesmo projeto, em algumas da o seguinte erro: An unhandled exception of type 'System.StackOverflowException' occurred in Microsoft.ReportViewer.Common.dll
na linha report.Render("Image".....)
Grato desde já!!
kra... antes de mais nda parabens pelo codigo... mto bom msm!
Mas eu me encontro com um problema... sempre q o compilador alcança a linha:
report.Render("Image", deviceInfo.ToString, _
AddressOf CreateStream, warnings)
ele gera um erro... no exception eu coloquei como tu falastes "ex.InnerException" e me retornou a seguinte mensagem:
"A definição do relatório 'rptXXX' não foi especificada"
ja busquei a internet toda pra solucionar esse problema... existem vários modos, mas nenhum conseguiu resolver esse caso :(
Tem como me dar uma ajuda?
obrigado
Ola Jorge, esta dando erro em LocalReprt e tbm em Warning.
não sei se é por estar a utilizar vs2010
Podes me ajudar?
obrigado
Private Sub Buttonprint_Click(sender As Object, e As EventArgs) Handles Buttonprint.Click
'''
''' Imprime o relatório
'''
Try
Dim rpt As New LocalReport
rpt.ReportPath = "Reportdetail.rdlc"
' ---------------------------------------------------------
' Definir DataSource, Parameters, etc para o relatório
' ---------------------------------------------------------
rpt.DataSources.Add(New ReportDataSource("databaseDataSet"))
Using cls As New ReportUtils
cls.Print(rpt)
End Using
Catch ex As Exception
MessageBox.Show(ex.Message, My.Application.Info.Title, _
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Boas
O que posso alterar esse codigo estou a tentar imprimir da esse erro:
"An error occured during local report processing"
Peço ajuda.
Enviar um comentário