VB .NET - Imprimindo o conteúdo de um controle RichTextBox


 Neste artigo vou mostrar como imprimir o conteúdo de um controle RichTextBox em aplicações VB .NET.

Este artigo foi baseado no original encontrado em : https://support.microsoft.com/pt-br/kb/811401 e acessado em agosto de 2016.

O controle RichTextBox não fornece um método para imprimir seu conteúdo. No entanto, você pode estender a classe RichTextBox para usar a mensagem EM_FORMATRANGE.

Nota: EM_FORMATRANGE é uma estrutura que o controle usa para formatar a saida para um dispositivo. (FORMATRANGE structure (Windows) - MSDN - Microsoft)

Você pode então enviar o conteúdo de um RichTextBox para um dispositivo de saída, como uma impressora.

Neste tutorial vou mostrar um exemplo prático implementando o código mostrado no artigo da Microsoft.

Recursos usados :

Criando o projeto Class Library no Visual Studio 2015 Community

Abra o VS 2015 Community e clique em New Project;

Selecione o template Class Library e informe o nome RichTextBoxPrintCtrl;

Altere o nome do arquivo Class1.vb para RichTextBoxPrintCtrl;

Na janela Solution Explorer clique com o botão direito do mouse em References e em seguida em Add Reference;

Na caixa de diálogo Add Reference selecione Assemblies e marque os namespaces System.Drawing e System.Windows.Forms;



Inclua o código abaixo no arquivo RichTextBoxPrintCtrl.vb :

Option Explicit On
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.Drawing.Printing
 
Namespace RichTextBoxPrintCtrl
    Public Class RichTextBoxPrintCtrl
        Inherits RichTextBox
 
        ' Converte a unidade que usada pelo .NET framework (1/100 inch) 
        ' e a unidade que é usada pelas chamadas da API Win32 (twips 1/1440 inch)
        Private Const AnInch As Double = 14.4
 
        <StructLayout(LayoutKind.Sequential)>
        Private Structure RECT
            Public Left As Integer
            Public Top As Integer
            Public Right As Integer
            Public Bottom As Integer
        End Structure
 
        <StructLayout(LayoutKind.Sequential)>
        Private Structure CHARRANGE
            Public cpMin As Integer          ' Primeiro caractere do intervalo (0 para inicio do doc)
            Public cpMax As Integer          ' Último caractere do intervalo (-1 para o fim do doc)
        End Structure
 
        <StructLayout(LayoutKind.Sequential)>
        Private Structure FORMATRANGE
            Public hdc As IntPtr             ' Atual DC para desenhar
            Public hdcTarget As IntPtr       ' Alvo DC para determinar a formatação do texto
            Public rc As RECT                ' Região do DC para desenhar(em twips)
            Public rcPage As RECT            ' Região de toda a DC (tam. da página) (em twips)
            Public chrg As CHARRANGE         ' Intervalo de texto para desenhar
        End Structure
 
        Private Const WM_USER As Integer = &H400
        Private Const EM_FORMATRANGE As Integer = WM_USER + 57
 
        Private Declare Function SendMessage Lib "USER32" Alias "SendMessageA" 
(ByVal hWnd As IntPtrByVal msg As IntegerByVal wp As IntPtrByVal lp As IntPtrAs IntPtr
 
        ' Renderiza o conteudo do RichTextBox para impressão
        '	Retorna o último caractere impresso +1 (a impressão inicia deste ponto para a próxim pagina)
        Public Function Print(ByVal charFrom As IntegerByVal charTo As IntegerByVal e As PrintPageEventArgsAs Integer
 
            ' Mark starting and ending character 
            Dim cRange As CHARRANGE
            cRange.cpMin = charFrom
            cRange.cpMax = charTo
 
            ' Calcula a área para renderizar e imprimir
            Dim rectToPrint As RECT
            rectToPrint.Top = e.MarginBounds.Top * AnInch
            rectToPrint.Bottom = e.MarginBounds.Bottom * AnInch
            rectToPrint.Left = e.MarginBounds.Left * AnInch
            rectToPrint.Right = e.MarginBounds.Right * AnInch
 
            ' Calcula o tamanho a pagina
            Dim rectPage As RECT
            rectPage.Top = e.PageBounds.Top * AnInch
            rectPage.Bottom = e.PageBounds.Bottom * AnInch
            rectPage.Left = e.PageBounds.Left * AnInch
            rectPage.Right = e.PageBounds.Right * AnInch
 
            Dim hdc As IntPtr = e.Graphics.GetHdc()
 
            Dim fmtRange As FORMATRANGE
            fmtRange.chrg = cRange                 ' Indicate character from to character to 
            fmtRange.hdc = hdc                     ' Use the same DC for measuring and rendering
            fmtRange.hdcTarget = hdc               ' Point at printer hDC
            fmtRange.rc = rectToPrint              ' Indicate the area on page to print
            fmtRange.rcPage = rectPage             ' Indicate whole size of page
 
            Dim res As IntPtr = IntPtr.Zero
 
            Dim wparam As IntPtr = IntPtr.Zero
            wparam = New IntPtr(1)
 
            ' Move o ponteiro da estrutura FORMATRANGE na memória
            Dim lparam As IntPtr = IntPtr.Zero
            lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange))
            Marshal.StructureToPtr(fmtRange, lparam, False)
 
            ' Send the rendered data for printing 
            res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam)
 
            ' Libera a memória alocada
            Marshal.FreeCoTaskMem(lparam)
 
            ' Libera o manipulador de contexto do dispositivo obtido na chamada anterior
            e.Graphics.ReleaseHdc(hdc)
 
            ' Retorna o ultimo caractere +1 da impressora
            Return res.ToInt32()
        End Function
 
    End Class
End Namespace

Observe que esta classe herda (Inherits) da classe RichTextBox e usa a estrutura EM_FORMATRANGE para implementar a impressão do seu conteúdo.

Salve e compile o projeto para gerar na pasta \bin\Debug o arquivo :  RichTextBoxPrintCtrl.dll que iremos usar para testar a nossa implementação.

Criando o projeto Windows Forms para imprimir o conteúdo do RichTextBox

No menu File clique em Add -> New Project;

Selecione o template Windows Forms Application e informe o nome RichTextBox_Impressao;

Agora vamos incluir uma referência ao nosso arquivo dll criado no projeto Class Library.

Clique com o botão direito sobre a ToolBox e a seguir clique em Choose Items;

Na janela Choose ToolBox Items clique na guia .NET Framework Components e a seguir clique em Browse;

Localize o arquivo RichTextBoxPrintCtrl.dll na pasta bin\Debug do projeto e selecione o arquivo e clique em Abrir;

Será incluída uma referência à DLL conforme mostrada na figura abaixo. Selecione a referência e clique em OK;

Agora o componente estará aparecendo na ToolBox. Arraste o componente para o formulário como mostra a figura abaixo:

A seguir inclua também no formulário os seguintes controles :

Na janela de propriedades, modifique a propriedade Document do PrintDialog1, PrintPreviewDialog1e PageSetupDialog1 para PrintDocument1.   

Exemplo:

 Disponha os controles conforme o leiaute da figura abaixo:

Inclua o código abaixo no evento Click dos botões :

1- Configurar Página

 Private Sub btnConfigurar_Click(sender As Object, e As EventArgs) Handles btnConfigurar.Click
        PageSetupDialog1.ShowDialog()
End Sub

2- Visualizar Impressão

 Private Sub btnVisualizar_Click(sender As Object, e As EventArgs) Handles btnVisualizar.Click
        PrintPreviewDialog1.ShowDialog()
 End Sub

3- Imprimir

 Private Sub btnImprimir_Click(sender As Object, e As EventArgs) Handles btnImprimir.Click
        If PrintDialog1.ShowDialog() = DialogResult.OK Then
            PrintDocument1.Print()
        End If
End Sub

No evento Click do botão Imagem vamos definir o código para incluir uma imagem no controle RichtTextBox conforme abaixo:

 Private Sub btnImagem_Click(sender As Object, e As EventArgs) Handles btnImagem.Click
        If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
            picImagem.Load(OpenFileDialog1.FileName)
            Clipboard.SetImage(picImagem.Image)
            picImagem.Image = Nothing
            Me.RichTextBoxPrintCtrl1.Paste()
        End If
End Sub

Agora no evento Click do botão Iniciar Dados inclua o código abaixo para preencher o controle com alguns dados:

 Private Sub btnIniciar_Click(sender As Object, e As EventArgs) Handles btnIniciar.Click
        With RichTextBoxPrintCtrl1
            .BackColor = Color.White
            .Clear()
            .BulletIndent = 10
            .SelectionFont = New Font("Georgia", 18, FontStyle.Bold)
            .SelectedText = "Macoratti .net " & vbLf
            .SelectionFont = New Font("Verdana", 16)
            .SelectionBullet = True
            .SelectionColor = Color.DarkBlue
            .SelectedText = "Quase Tudo" + vbLf
            .SelectionFont = New Font("Verdana", 14)
            .SelectionColor = Color.Orange
            .SelectedText = "Para VB.NET" + vbLf
            .SelectionFont = New Font("Verdana", 12)
            .SelectionColor = Color.Green
            .SelectedText = "C# , ASP .NET " + vbLf
            .SelectionColor = Color.Red
            .SelectedText = "e outros recursos da plataforma .NET" + vbLf
            .SelectionBullet = False
            .SelectionFont = New Font("Tahoma", 10)
            .SelectionColor = Color.Black
            .SelectedText = "http://www.macoratti.net" & vbLf
        End With
    End Sub

Finalmente inclua o código abaixo no formulário onde

Private verificaImpressao As Integer
    Private Sub PrintDocument1_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.BeginPrint
        verificaImpressao = 0
    End Sub
    Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
        ' imprime o conteudo do RichTextBox. 
        ' armazena o ultimo caractere impresso
        verificaImpressao = RichTextBoxPrintCtrl1.Print(verificaImpressao, RichTextBoxPrintCtrl1.TextLength, e)
        ' verifica se há mais paginas
        If verificaImpressao < RichTextBoxPrintCtrl1.TextLength Then
            e.HasMorePages = True
        Else
            e.HasMorePages = False
        End If
  End Sub

Agora é só alegria...

Executando o projeto, iniciando os dados e incluindo uma imagem ao clicar no botão para Visualizar Impressão iremos obter o seguinte resultado:

Pegue o projeto completo aqui :  RichTextBoxPrintCtrl.zip

"Verdadeiramente ele (Jesus) tomou sobre si as nossas enfermidades, e as nossas dores levou sobre si; e nós o reputávamos por aflito, ferido de Deus, e oprimido."
Isaías 53:4

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 ?

Referências:


José Carlos Macoratti