Visual Basic .NET - Imprimindo com PrintDocument


Já estamos usando na versão do  VB .NET 2008 e a impressão no Visual Basic ainda é um tema que gera muitas discussões e dúvidas. Afinal , apesar de todos os esforços não temos ainda uma ferramenta nativa no Visual Basic para impressão que atenda as necessidades diárias de um desenvolvedor que usa a linguagem. Geralmente somos forçados a recorrer a componentes de terceiros , e , aos custos que isso acarreta.

Para saber mais sobre esse assunto veja os meus artigos já publicados:

Neste artigo eu vou procurar complementar as informações já publicadas aplicadas ao Visual Basic 2008 (estou usando a versão Express).

Já sabemos que a classe PrintDocument é o coração do processo de impressão no Visual Basic e ao usá-la em seu programa será criada uma instância desta classe com os eventos que serão usados para a impressão.

O objeto PrintDocument dispara quatro eventos chaves básicos:

  1. BeginPrint - Este evento é lançado quanto a impressão vai começar; neste momento o programa pode iniciar estrutura de dados, carregar dados, efetuar a conexão com a fonte de dados e realizar qualquer outra operação necessária antes de iniciar a impressão;
  2. QueryPageSettings - Este evento é acionado antes do programa imprimir uma página. Neste momento o programa pode modificar os dados do documento como alterar as margens da página, etc.;
  3. PrintPage - Este evento ocorre para gerar a página. O programa necessita capturar este evento e usar o objeto Graphics fornecido pelos parâmetros do manipulador de eventos para gerar a saída do relatório , ao final o manipulador de eventos deverá definir o valor de e.HashMorePages para True ou False e devolver para o objeto PrintDocument informando se existem mais páginas a serem geradas;
  4. EndPrint - Este evento será lançado ao término da impressão. Ele pode ser capturado para liberar os recursos usados, fechar conexões, etc.;

Depois que você criou um objeto PrintDocument e definiu os manipuladores de eventos você pode fazer 3 coisas com ele:

  1. Chamar o método Print do objeto para enviar o documento de imediato para a impressão padrão selecionada;
  2. Definir um controle PrintPreviewDialog e atribuir a sua propriedade Document um objeto PrintDocument e então chamar o método ShowDialog do controle. O controle PrintPreviewDialog exibe a janela para visualizar a impressão e usar o objeto PrintDocument para gerar a saída que será exibida. O controle possui um botão para enviar o documento para a impressora;

Nota:  O controle PrintPreviewControl exibe uma janela de visualização da impressão com a diferença que ele fica no seu formulário e não apresenta os mesmos botões  do PrintPreviewDialog mas possui métodos que permitem simular o mesmo comportamento.

  1. Definir um controle PrintDialog e atribuir a sua propriedade Document um objeto PrintDocument e então chamar o método ShowDialog do controle. O usuário pode selecionar a impressora e definir suas propriedades , quando o usuário clicar na opção para imprimir o objeto PrintDocument envia o documento para impressora;

Vejamos a aplicação destes recursos em um exemplo no Visual Basic 2008 Express Edition.

Abra o VB 2008 Express Edition (você pode usar o VS 2008) e crie um novo projeto do tipo Windows Application com o nome imprimindoNet;

Defina no formulário três botões de comando - Button1, Button2  e Button3 -  no formulário form1.vb e, a partir da ToolBox, inclua os componentes: PrintDocument, PrintPreviewDialog e PrintDialog conforme abaixo:

Agora defina o namespace para impressão:

Imports System.Drawing.Printing

Defina também o objeto PrintDocument usando a palavra-chave WithEvents de forma a capturar mais facilmente os eventos do objeto;

Private WithEvents m_PrintDocument As PrintDocument

Agora inclua o código abaixo em cada um dos eventos Click dos botões de comando:( A direita temos o resultado para os botões de visualização)

'imprimir direto

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles
Button1.Click

  m_PrintDocument = New PrintDocument

  m_PrintDocument.Print()

End Sub

 

 

'criar visualizacao da impressao

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles
Button2.Click

  m_PrintDocument = New PrintDocument
 

  PrintPreviewDialog1.Text = "Usando - PrintPreviewDialog"

  PrintPreviewDialog1.Document = m_PrintDocument

  PrintPreviewDialog1.ShowDialog()

End Sub

 

'criar dialogo de impressao

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles
Button3.Click

  m_PrintDocument = New PrintDocument
 

  PrintDialog1.Document = m_PrintDocument

  PrintDialog1.ShowDialog()

End Sub

Só falta definir uma rotina para imprimir algo no evento PrintPage;

'gerar a pagina e imprimir

Private Sub m_PrintDocument_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles m_PrintDocument.PrintPage


Using caneta As New Pen(Color.Black, 20)

    e.Graphics.DrawRectangle(caneta, e.MarginBounds)

    caneta.DashStyle = Drawing2D.DashStyle.Dash

    caneta.Alignment = Drawing2D.PenAlignment.Outset

    e.Graphics.DrawRectangle(caneta, e.PageBounds)

End Using


'¡ndica que nao ha  mais paginas a serem impressas

e.HasMorePages = False

End Sub

 

E pronto, temos uma visão de como tratar os objetos para impressão de forma básica.

Vamos agora então mostrar algo mais prático. Vamos criar uma rotina para imprimir o conteúdo de uma tabela a partir de um formulário em uma aplicação Windows.

Vamos criar um banco de dados no Microsoft Access chamado Teste.mdb e uma tabela chamada Clientes com os seguintes campos:

  1. codigo : Numeração Automática e chave Primária
  2. nome  :  Texto - 250

Inclua também alguns dados na tabela para efetuar os testes.

Abra o Visual Basic 2008 Express Edition e crie um novo projeto do tipo Windows Froms Application com o nome de impressaoTeste;

A seguir crie um novo DataSource na opção Data -> Add New DataSource;

Selecione o objeto DataBase e defina a conexão com o banco de dados Teste.mdb;

Selecione a tabela Clientes e clique no botão FInish;

Agora selecione o formulário form1.vb e abra a janela Data Sources. Defina o modo DataGridView para o mesmo e arraste e solte-o no formulário. Será criado um DataGridView e toda a estrutura para manutenção dos dados. Inclua também um componente PrintDocument a partir da ToolBox;

Inclua na barra de navegação do BindingNavigator um novo ícone para a impressão dos dados:

Agora vamos usar o evento Click relacionado ao ícone para imprimir os dados da tabela Clientes.

Primeiro defina os namespaces usados no projeto:

Imports System.Data

Imports System.Data.OleDb

Imports System.Drawing.Printing

A seguir defina as variáveis usadas :

Dim cmd As OleDbCommand

Private paginaAtual As Integer = 1

Private MyConnection As OleDbConnection

Private Leitor As OleDbDataReader

Private RelatorioTitulo As String

Agora vamos ao código...

No evento Click do ToolStripButton devemos incluir o seguinte código que será responsável pela definição dos objetos e eventos de impressão:

Private Sub ToolStripButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton1.Click
 

'obtem a string de conexao

MyConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\teste\Teste.mdb")


'define o titulo do relatorio

RelatorioTitulo = "Lista de Clientes - "


'define os objetos printdocument e os eventos associados

Dim pd As Printing.PrintDocument = New Printing.PrintDocument()

 

'IMPORTANTE - definimos 3 eventos para tratar a impressão : PringPage, BeginPrint e EndPrint.

AddHandler pd.PrintPage, New Printing.PrintPageEventHandler(AddressOf Me.pdRelatorios_PrintPage)

AddHandler pd.BeginPrint, New Printing.PrintEventHandler(AddressOf Me.Begin_Print)

AddHandler pd.EndPrint, New Printing.PrintEventHandler(AddressOf Me.End_Print)


'define o objeto para visualizar a impressao

Dim objPrintPreview As New PrintPreviewDialog


Try

'define o formulário como maximizado e com Zoom

With objPrintPreview

   .Document = pd

   .WindowState = FormWindowState.Maximized

   .PrintPreviewControl.Zoom = 1

   .Text = "Relacao de Clientes"

   .ShowDialog()

End With

Catch ex As Exception

MessageBox.Show(ex.ToString())

End Try

End Sub

No evento Begin_Print vamos abrir o DataReader com os dados da tabela Clientes;

'A conexÆo e o DataReader ‚ aberto aqui

Private Sub Begin_Print(ByVal sender As Object, ByVal e As Printing.PrintEventArgs)


Dim Sql As String = "SELECT * from clientes"

Dim MyComand As New OleDbCommand(Sql, MyConnection)


MyConnection.Open()

Leitor = MyComand.ExecuteReader()

paginaAtual = 1


End
Sub

Agora temos a rotina mais trabalhosa, onde iremos definir o leiaute da página, a fonte, as margens, os campos a serem impressos e a imagem a ser impressa;

'Layout da(s) p gina(s) a imprimir

Private Sub pdRelatorios_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)
 

'Variaveis das linhas

Dim LinhasPorPagina As Single = 0

Dim PosicaoDaLinha As Single = 0

Dim LinhaAtual As Integer = 0


'Variaveis das margens

Dim MargemEsquerda As Single = e.MarginBounds.Left

Dim MargemSuperior As Single = e.MarginBounds.Top + 100

Dim MargemDireita As Single = e.MarginBounds.Right

Dim MargemInferior As Single = e.MarginBounds.Bottom

Dim CanetaDaImpressora As Pen = New Pen(Color.Black, 1)

Dim codigo As Integer

Dim nome As String


'Variaveis das fontes

Dim FonteNegrito As Font

Dim FonteTitulo As Font

Dim FonteSubTitulo As Font

Dim FonteRodape As Font

Dim FonteNormal As Font


'define efeitos em fontes

FonteNegrito = New Font("Arial", 9, FontStyle.Bold)

FonteTitulo = New Font("Arial", 15, FontStyle.Bold)

FonteSubTitulo = New Font("Arial", 12, FontStyle.Bold)

FonteRodape = New Font("Arial", 8)

FonteNormal = New Font("Arial", 9)


'define valores para linha atual e para linha da impressao

LinhaAtual = 0

Dim L As Integer = 0


'Cabecalho

e.Graphics.DrawLine(CanetaDaImpressora, MargemEsquerda, 60, MargemDireita, 60)

e.Graphics.DrawLine(CanetaDaImpressora, MargemEsquerda, 160, MargemDireita, 160)
'nome da empresa

e.Graphics.DrawString(My.Settings.Empresa, FonteTitulo, Brushes.Blue, MargemEsquerda + 250, 80, New StringFormat())


'Imagem

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

e.Graphics.DrawString(RelatorioTitulo & System.DateTime.Today, FonteSubTitulo, Brushes.Black, MargemEsquerda + 250, 120, New StringFormat())
 

'campos a serem impressos: Codigo e Nome

e.Graphics.DrawString("Cod.", FonteNegrito, Brushes.Black, MargemEsquerda + 100, 170, New StringFormat())

e.Graphics.DrawString("Nome", FonteNegrito, Brushes.Black, MargemEsquerda + 200, 170, New StringFormat())

e.Graphics.DrawLine(CanetaDaImpressora, MargemEsquerda, 190, MargemDireita, 190)

 

LinhasPorPagina = CInt(e.MarginBounds.Height / FonteNormal.GetHeight(e.Graphics) - 9)

 

'Aqui sao lidos os dados

While (LinhaAtual < LinhasPorPagina AndAlso Leitor.Read())

 

'obtem os valores do datareader

codigo = Leitor.GetInt32(0)

nome = Leitor.GetString(1)

 

'inicia a impressao

PosicaoDaLinha = MargemSuperior + (LinhaAtual * FonteNormal.GetHeight(e.Graphics))

e.Graphics.DrawString(codigo.ToString(), FonteNormal, Brushes.Black, MargemEsquerda + 100, PosicaoDaLinha, New StringFormat())

e.Graphics.DrawString(nome.ToString, FonteNormal, Brushes.Black, MargemEsquerda + 200, PosicaoDaLinha, New StringFormat())

LinhaAtual += 1

End While

'Rodape

e.Graphics.DrawLine(CanetaDaImpressora, MargemEsquerda, MargemInferior, MargemDireita, MargemInferior)

e.Graphics.DrawString(System.DateTime.Now.ToString(), FonteRodape, Brushes.Black, MargemEsquerda, MargemInferior, New StringFormat())

LinhaAtual += CInt(FonteNormal.GetHeight(e.Graphics))

LinhaAtual += 1

e.Graphics.DrawString("P gina : " & paginaAtual, FonteRodape, Brushes.Black, MargemDireita - 50, MargemInferior, New StringFormat())

 

'Incrementa o n£mero da pagina

paginaAtual += 1

 

'verifica se continua imprimindo

If (LinhaAtual > LinhasPorPagina) Then

     e.HasMorePages = True

Else

    e.HasMorePages = False

End If

End Sub

 

E, finalmente a rotina para fechar o datareader e a conexão:

'Encerra a conexÆo e o DataReader

Private Sub End_Print(ByVal sender As Object, ByVal byvale As Printing.PrintEventArgs)

   Leitor.Close()

   MyConnection.Close()

End Sub

 

Executando o projeto e clicando no ícone para imprimir temos:

Como o código esta comentado não nada a a acrescentar, a não ser informar que a variável Empresa que exibe o nome da empresa foi definida no arquivo de configuração da aplicação.   

Como podemos ver o VB 2008 apresenta praticamente o mesmo resultado em relação a impressão de documentos. Quem sabe na nova versão alguma novidade boa neste sentido pode surgir , até lá vamos ter que fazer malabarismos para imprimir no VB se não quisermos usar nenhum gerador de relatório externo.

Agora para encerrar duas dicas legais recebidos do colega Ivo Closs:

Existe uma forma de deixar o menu (toolstrip) com uma aparência melhor (o original não tem apelo visual nenhum).

Basta inserir antes do ShowDialog() alguma linhas para mudar o estilo visual - veja script em negrito.

Dim newPrintPreviewDialog As New PrintPreviewDialog
Try
    'define o formulário como maximizado e com Zoom
    newPrintPreviewDialog.Document = newPrintDocument
    newPrintPreviewDialog.WindowState = FormWindowState.Maximized
    newPrintPreviewDialog.PrintPreviewControl.Zoom = 1
    newPrintPreviewDialog.Text = "Relacao de Clientes"

   
' Seta o estilo visual
    CType(newPrintPreviewDialog.Controls(1), ToolStrip).RenderMode = ToolStripRenderMode.Professional
    CType(newPrintPreviewDialog.Controls(1), ToolStrip).GripStyle = ToolStripGripStyle.Visible
    'CType(newPrintPreviewDialog.Controls(1), ToolStrip).Items.Item(0).Image = My.Resources.PrintToolStripMenuItem_Image
    'CType(newPrintPreviewDialog.Controls(1), ToolStrip).Items.Item(1).Image = My.Resources.zoom
    CType
(newPrintPreviewDialog.Controls(1), ToolStrip).Height = 25

    newPrintPreviewDialog.ShowDialog()

Catch ex As Exception
    MessageBox.Show(ex.ToString())
End Try

 

Uma dica para imprimir valores numéricos, alinhados à direita:

'imprime o código com alinhamento a direita
Dim drawFormat As New StringFormat
drawFormat.FormatFlags = StringFormatFlags.DirectionRightToLeft
e.Graphics.DrawString(iCodigo.ToString(
"#####"), FonteNormal, Brushes.Black, MargemEsquerda + 125, PosicaoDaLinha, drawFormat)

Pegue o projeto completo aqui:   testeImpressao.zip

Eu sei é apenas VB.NET mas eu gosto.

Até o próximo artigo...

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 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

 

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

 

Referências:


José Carlos Macoratti