Visual Basic em Português

Página pessoal de Jorge Paulino sobre o Visual Basic (VB.NET, ASP.NET, VB6, VBA) e algumas noticias de tecnologia

VB.NET: Microsoft Reports – Imprimir sem utilizar o ReportViewer

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!

13 comentários:

Anónimo disse...

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.

Jorge Paulino disse...

Olá,

Verifica qual a mensagem que tens no inner exception quando o erro ocorre.

Luiz Morgado disse...

Bom Dia amigo, muito bom o Post, funcionou perfeitamente, Obrigado.

Anónimo disse...

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

Jorge Paulino disse...

Olá Eduardo,

Sim claro, é preciso definir a datasource do relatório.

Vê este artigo que mostra como o fazer: VB.NET: Microsoft Reporting Services

Anónimo disse...

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

Alberto Cláudio disse...

Fantastico.
funcionou perfeitamente. Cheguei até a alterar um pouco o código com a hipótese de alterar a impressora em que desejo imprimir

Anónimo disse...

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

Jorge Paulino disse...

Olá Giovani,

Qual é o erro que dá na inner exception?

Em vez de ex.Message usar ex.InnerException

Anónimo disse...

Ola Jorge, esta dando erro em LocalReprt e tbm em Warning.

Podes me ajudar????

Anónimo disse...

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

André Guilhem disse...

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

Anónimo disse...

Ola Jorge, esta dando erro em LocalReprt e tbm em Warning.
não sei se é por estar a utilizar vs2010
Podes me ajudar?
obrigado

Mensagens Recentes



Microsoft Office Especialist

Membro da Comunidade
Experts-Exchange


Administ. da Comunidade
Portugal-a-Programar



Twitter

Artigos no CodeProject

Artigos no CodeProject

Subscrever Novidades

Endereço de Email:

Delivered by FeedBurner

Seguidores

Histórico