VB .NET - Impressão usando o System.Drawing.Printing


Uma das grandes lacunas existentes no VB é impressão. Com a versão do VB5 e VB6 tínhamos as seguintes opções para gerar relatórios e imprimir documentos:

A respeito deste panorama sombrio com relação a impressão no VB eu tenho duas notícias: uma boa e uma ruim. A ruim é que o VB .NET não resolveu todos os problemas existentes , e a boa é que ficou muito mais fácil imprimir documentos e gerar relatórios no VB .NET.

Neste artigo vou procurar mostrar como podemos usar o namespace System.Drawing.Printing , o sucessor do objeto Printer do VB , para criar relatórios usando código VB.NET.

Para começar vamos dar uma olhada nas principais classes para impressão .

PrintDocument :

PrinterSettings : Fornece informações sobre como um documento será impresso.

PageSettings : Fornece informações sobre como uma página será impressa.

PrintPageEventArgs : Dados para o evento PrintPage de PrintDocument.

PrintEventArgs : Dados para os eventos BeginPrint e EndPrint em um PrintDocument. Permite cancelar o trabalho de impressão.

PrintPreviewControl : Um controle que exibe um PrintDocument. Permite criar um diálogo de visualizar a impressão.

PrintPreviewDialog : Diálogo que exibe um PrintDocument usando o PrintPreviewControl.

PageSetupDialog : Diálogo das propriedades da página.

PrintController : Controla um PrintDocument que é impresso. Temos dois PrintController

Basicamente a lógica para uma impressão até feita usando os eventos de PrintDocument . Quando o método PrintDocument.Print() é chamado temos a seguinte sequência de eventos:

  1. BeginPrint
  2. PrintPage
  3. EndPrint

O tipo de argumentos do evento PrintPage possui a propriedade HasMorePages. Se ela for definida como True quando o evento retornar , PrintDocument define uma nova página e levanta o evento PrintPage novamente. Desta forma a lógica do seu código para impressão no evento PrintPage deverá ser basicamente a seguinte :

Da teoria para a prática

Vou mostrar como podemos criar relatórios via código usando o System.Drawing.Printing usando como exemplo a criação de um relatório que exibe os dados contidos em uma tabela de um banco de dados. Iremos portanto realizar as seguintes tarefas:

Para não termos muito trabalho vou escolher uma tabela de um banco de dados já existente. O escolhido é o já famoso saco de pancadas Northwind.mdb . Iremos acessar a tabela produtos deste banco de dados e exibir em um relatório os campos : Código do Produto, Nome do Produto , Preço Unitário

A estrutura da tabela produtos do banco de dados Northwind.mdb é a seguinte :

 O layout do relatório deverá ser algo parecido com :

O acesso aos dados

Como iremos montar um relatório que vai exibir os dados da tabela Pedidos teremos que criar uma conexão e extrair os dados que desejemos exibir via instrução SQL.

Como iremos acessar uma base de dados Access irei usar o namespace System.Data.OleDb e o provedor  "Provider=Microsoft.Jet.OLEDB.4.0;".

Vamos utilizar um objeto DataReader para obter as informações da tabela via objeto OleDbCommand.

Conforme já mostrei no artigo - ADO.NET - Uma visão geral  I :  Objetos Connection ,  Command e DataReader - o código para conexão com uma base de dados Access em c:\teste\northwind.mdb usando um DataReader pode ser expresso assim :

 

Dim conn As New OleDbConnection()
conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\teste\northwind.mdb"
Dim cmd As OleDbCommand = conn.CreateCommand

cmd.CommandText = "Select CódigoDoProduto,NomeDoProduto, PreçoUnitário from Produtos"

conn.Open()
Dim leitor As OleDbDataReader = cmd.ExecuteReader()

Try
    While leitor.Read()
                'código para visualizar o relatório
    End While
    leitor.Close()
    conn.Close()

Catch erro As Exception
        MsgBox("Erro " & vbCrLf & erro.ToString, MsgBoxStyle.Critical, "Erro")
End Try
End Sub

 

Apenas para recordar temos que :

No interior loop While/End While iremos é que vamos colocar o código que permite a visualização da impressão pois e no interior do loop que estaremos lendo os registros da tabela Produtos. Devemos fazer o seguinte:

Nota: Para saber mais leia o artigo :  Impressão e Visualização. O que há de novo ?

O código deverá ficar assim :

        While drProdutos.Read
            'cria um novo documento para impressão
            Dim pd As PrintDocument = New PrintDocument()
           'relaciona o objeto pd ao procedimento rptProdutos
            AddHandler pd.PrintPage, AddressOf Me.rptProdutos
            'cria uma nova instância do objeto PrintPreviewDialog()
            objPrintPreview = New PrintPreviewDialog()
            'define algumas propriedades do obejto
            With objPrintPreview
                 'indica qual o documento vai ser visualizado
                .Document = pd
                .WindowState = FormWindowState.Maximized
                .PrintPreviewControl.Zoom = 1   'maxima a visualização
                .Text = "Catálogo de Produtos"
                 'exibe a janela de visualização para o usuário
                .ShowDialog()
            End With
        End While

A única novidade seria o código que relaciona o objeto PrintDocument com o evento rptProdutos :

 AddHandler pd.PrintPage, AddressOf Me.rptProdutos

Aqui estamos usando a declaração AddHandler e o operador AddressOf .

Neste caso estou associando o procedimento rptProdutos ao evento pd.PrintPage , ou seja , o procedimento rptProdutos irá os dados enviados pelo objeto DataReader e gerará o relatório formatado para visualização/impressão.

Agora só falta definir o código do procedimento rptProdutos. Embora seja um pouco trabalhoso o código é simples. Vamos a ele:

1- Primeiro vamos definir o procedimento que irá receber os dados do objeto DataReader sua declaração é a seguinte:

 Private Sub rptProdutos(ByVal sender As Object, ByVal Relatorio As System.Drawing.Printing.PrintPageEventArgs)

O procedimento possui um argumento do tipo PrintPageEventArgs chamado de Relatorio que iremos usar para definir a formatação do relatório através de cabeçalho, rodapé , título , fontes , linhas , imagens , texto, etc.

Antes de prosseguir vamos assumir que: Estamos imprimindo na impressora padrão. Agora vamos definir os itens que irão formatar o nosso relatório:

- Margens : vamos assumir as margens definidas pela impressora padrão para isto vamos usar a propriedade MarginBounds do objeto

Dim margemEsq As Single = Relatorio.MarginBounds.Left

Dim margemSup As Single = Relatorio.MarginBounds.Top

Dim margemDir As Single = Relatorio.MarginBounds.Right

Dim margemInf As Single = Relatorio.MarginBounds.Bottom

- Fontes : você pode usar a fonte que desejar com tamanho e estilos a sua escolha. Eu vou trabalhar com a fonte Verdana definida para Título , rodapé e normal. Para isto vou usar a classe Font.

Dim fonteTitulo As Font

Dim fonteRodape As Font

Dim fonteNormal As Font
 

fonteTitulo = New Font("Verdana", 15, FontStyle.Bold)

fonteRodape = New Font("Verdana", 8)

fonteNormal = New Font("Verdana", 10)

- Linhas , imagens e texto : Para imprimir linhas , imagens e/ou texto vamos usar os métodos do objeto PrintPageEventArgs(relatorio) conforme abaixo:

- Linhas - Para imprimir um a linha reta entre dois pontos usa o método : relatorio.Graphics.DrawLine - DrawLine ( pen , point1, point2) :

- Imagens - Para imprimir uma imagem (bmp,wmf, gif ou jpg) usamos o método: relatorio.Graphics.DrawImage - DrawImage(image,point):

- Textos - Para imprimir uma string usamos o método : relatorio.Graphics.DrawString - DrawString( string ,font, brush, point, format)

Então a seguir vou mostrar como podemos imprimir as secções do relatório usando os métodos e propriedades já vistos até aqui.

- Impressão do cabeçalho: Abaixo temos uma imagem do cabeçalho que desejamos:

e a seguir o código que implementa isto :

Relatorio.Graphics.DrawLine(caneta, margemEsq, 60, margemDir, 60)

Relatorio.Graphics.DrawImage(Image.FromFile("c:\teste\" & "maco10.gif"), 100, 68)

Relatorio.Graphics.DrawLine(caneta, margemEsq, 160, margemDir, 160)

Relatorio.Graphics.DrawString("Catálogo de Produtos", fonteTitulo, Brushes.Blue, margemEsq + 275, 80, New StringFormat())

 

- Impressão dos títulos das colunas do relatório.

 

O  título das colunas irá exibir os nomes : Código , Produto e Preço no relatório. O código esta abaixo:

 

'impressão do titulo das colunas

Relatorio.Graphics.DrawString("Código", fonteColuna, Brushes.Red, margemEsq, 140, New StringFormat())

Relatorio.Graphics.DrawString("Produto", fonteColuna, Brushes.Red, margemEsq + 100, 140, New StringFormat())

Relatorio.Graphics.DrawString("Preço", fonteColuna, Brushes.Red, margemEsq + 500, 140, New StringFormat())

Relatorio.Graphics.DrawLine(caneta, margemEsq, 170, margemDir, 170)

 

 

o resultado é :

Nota : As variáveis leitor e paginaAtual declaradas de forma serem visíveis pelo projeto:

Private leitor As OleDbDataReader

Private paginaAtual As Integer = 1

Precisamos definir o número de linhas por página conforme a área de impressão , assim poderemos ajeitar o rodapé na página. O código para isto é o seguinte:

'define o número de linhas por página

'para isto faço a divisão da área de impressão pelo tamanho da fonte subtraido do valor 10

linhasPorPagina = Relatorio.MarginBounds.Height / fonteNormal.GetHeight(Relatorio.Graphics) - 10

Vamos a seguir atribuir os dados da fonte de dados para as variáveis já definidas

'para imprimir os dados da base de dados no relatório teremos que atribuir as variáveis os valores

'através do objeto OleDbDatareader

codigoProduto = leitor(0) 'codigo do produto

nomeProduto = leitor(1) 'nome do produto

precoProduto = leitor(2) 'preco do produto

 

Finalmente iremos iniciar o laço While/End e ler enquanto a linha atual for menor que o número de linhas por página e houver dados. Quando o número de linhas atual for maior que o permitido iremos criar o rodapé , verificar se ainda há dados para serem lidos e , se for o caso , continuar a impressão na próxima página.

'agora vamos dar um laço através dos registros do DataReader levando em conta o número de linhas

'permitido para a página. Enquanto a linha atual for menor que o número de linhas por página e não

'final de arquivo estaremo no loop

While (linhaAtual < linhasPorPagina And leitor.Read())

 

'acompanha a posição da linha atual

posicaoDaLinha = margemSup + (linhaAtual * fonteNormal.GetHeight(Relatorio.Graphics))

 

'imprime os dados relativo ao codigo , nome do produto e preço do produto

Relatorio.Graphics.DrawString(codigoProduto, fonteNormal, Brushes.Black, margemEsq, posicaoDaLinha, New StringFormat())

Relatorio.Graphics.DrawString(nomeProduto, fonteNormal, Brushes.Black, margemEsq + 100, posicaoDaLinha, New StringFormat())

Relatorio.Graphics.DrawString(FormatCurrency(precoProduto), fonteNormal, Brushes.Black, margemEsq + 500, posicaoDaLinha, New StringFormat())

 

'faz o incremento no número de linha

linhaAtual += 1

 

'verifica se ainda podemos imprimir , ou seja , se a linha atual é menor que o número

'de linhas permitido pela página. Se for continuamos a atribuir os dados e a imprimir

If (linhaAtual < linhasPorPagina) Then

   codigoProduto = leitor(0) 'codigo do produto

   nomeProduto = leitor(1) 'nome do produto

   precoProduto = leitor(2) 'preco do produto

End If

 

End While

'imprime o rodape no relatorio

Relatorio.Graphics.DrawLine(caneta, margemEsq, margemInf, margemDir, margemInf)

Relatorio.Graphics.DrawString(System.DateTime.Now, fonteRodape, Brushes.Black, margemEsq, margemInf, New StringFormat())

Relatorio.Graphics.DrawString("Pag. " & paginaAtual.ToString, fonteRodape, Brushes.Black, margemDir - 50, margemInf, New StringFormat())

'incrementa a página atual

paginaAtual += 1

 

'vamos verificar se ainda existem registros para serem impressos

If (leitor(0) <> Nothing) Then

   Relatorio.HasMorePages = True

Else

   Relatorio.HasMorePages = False

End If

End Sub

 

O resultado da visualização da impressão será o seguinte :

Usando as classes do System.Drawing.Printing podemos criar relatórios completos com recursos de visualização sem precisar recorrer a soluções de terceiros.

Até o próximo artigo VB.NET

Referências:

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti