11 10 2017
Imprimindo informações direto na impressora com C#
Alguns anos atrás eu escrevi um artigo sobre impressão direto na impressora com C#. Esse artigo acabou ficando bastante popular aqui no site, por isso resolvi gravar uma versão em vídeo desse artigo. Aqui vai o resultado:
Listando as impressoras instaladas
A lista de impressoras instaladas no sistema operacional está disponível no .NET a partir da propriedade “InstalledPrinters” da classe “PrinterSettings“, que fica localizada dentro do namespace “System.Drawing.Printing“:
// C# public Form1() { InitializeComponent(); CarregarImpressoras(); } private void CarregarImpressoras() { impressoraComboBox.Items.Clear(); foreach (var impressora in System.Drawing.Printing.PrinterSettings.InstalledPrinters) { impressoraComboBox.Items.Add(impressora); } }
' VB.NET Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load CarregarImpressoras() End Sub Private Sub CarregarImpressoras() ImpressoraComboBox.Items.Clear() For Each Impressora In System.Drawing.Printing.PrinterSettings.InstalledPrinters ImpressoraComboBox.Items.Add(Impressora) Next End Sub
Imprimindo texto em uma página
Para imprimirmos informações direto na impressora no .NET, temos que utilizar a classe “PrintDocument“. Uma vez criada uma instância dessa classe, temos que implementar o código do evento “PrintPage” e chamar o método “Print” para iniciar a impressão. O evento “PrintPage” será chamado uma vez para cada página a ser impressa:
// C# private void imprimirButton_Click(object sender, EventArgs e) { using (var pd = new System.Drawing.Printing.PrintDocument()) { pd.PrinterSettings.PrinterName = impressoraComboBox.SelectedItem.ToString(); pd.PrintPage += Pd_PrintPage; pd.Print(); } } private void Pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { }
' VB.NET Private Sub ImprimirButton_Click(sender As Object, e As EventArgs) Handles ImprimirButton.Click Using Pd = New System.Drawing.Printing.PrintDocument() Pd.PrinterSettings.PrinterName = ImpressoraComboBox.SelectedItem.ToString() AddHandler Pd.PrintPage, AddressOf Pd_PrintPage Pd.Print() End Using End Sub Private Sub Pd_PrintPage(sender As Object, e As System.Drawing.Printing.PrintPageEventArgs) End Sub
Dentro do evento “PrintPage“, nós temos à nossa disposição a área gráfica da página (que fica acessível através do parâmetro do evento “e.Graphics“). Com esse objeto do tipo “Graphics“, nós conseguimos desenhar figuras geométricas, imagens e texto, utilizando os métodos “Draw*“:
O método para desenharmos texto na página é o “DrawString“. Além do texto, nós precisamos passar como parâmetro desse método a fonte, cor e área onde o texto deverá ser impresso na página. Nesse exemplo, vamos criar uma instância da fonte “Times New Roman” tamanho 14 e um “SolidBrush” de cor preta. O texto deverá ser impresso na área total da página, desconsiderando as margens (essa informação está disponível através dos parâmetros do evento – “e.MarginBounds“):
// C# private void Pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { using (var font = new Font("Times New Roman", 14)) using (var brush = new SolidBrush(Color.Black)) { e.Graphics.DrawString(impressaoTextBox.Text, font, brush, e.MarginBounds); } }
' VB.NET Private Sub Pd_PrintPage(sender As Object, e As System.Drawing.Printing.PrintPageEventArgs) Using Font = New Font("Times New Roman", 14) Using Brush = New SolidBrush(Color.Black) e.Graphics.DrawString(ImpressaoTextBox.Text, Font, Brush, e.MarginBounds) End Using End Using End Sub
Imprimindo texto em múltiplas páginas
A implementação apresentada acima funciona muito bem desde que o texto caiba em uma única página. Caso o texto seja maior, aí temos que fazer alguns ajustes. Primeiramente, temos que calcular a quantidade de texto que poderá ser impressa em cada página. Isso pode ser feito através do método “MeasureString” da classe “Graphics“. Esse método retornará a quantidade de caracteres que cabem em uma determinada área da página.
Além disso, teremos que criar uma variável no nível do formulário para controlarmos o texto que ainda está faltando para ser impresso. Essa variável será inicializada antes da impressão com o texto completo e será reduzida a cada página que for sendo impressa.
Por fim, nós precisamos informar de alguma maneira para o PrintDocument que existem mais páginas para serem impressas. Isso é feito através da propriedade “e.HasMorePages“, que deve ser configurada como “true” caso ainda exista texto para ser impresso. Quando essa propriedade é configurada como “true“, o evento “PrintPage” será disparado novamente e o restante do texto será impresso na próxima página.
Veja só como fica o código completo com suporte a múltiplas páginas:
// C# private void imprimirButton_Click(object sender, EventArgs e) { using (var pd = new System.Drawing.Printing.PrintDocument()) { pd.PrinterSettings.PrinterName = impressoraComboBox.SelectedItem.ToString(); pd.PrintPage += Pd_PrintPage; _texto = impressaoTextBox.Text; pd.Print(); } } private string _texto; private void Pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { using (var font = new Font("Times New Roman", 14)) using (var brush = new SolidBrush(Color.Black)) { int caracteres = 0; int linhas = 0; e.Graphics.MeasureString(_texto, font, new Size(e.MarginBounds.Width, e.MarginBounds.Height - font.Height + 1), StringFormat.GenericTypographic, out caracteres, out linhas); e.Graphics.DrawString(_texto.Substring(0, caracteres), font, brush, e.MarginBounds); _texto = _texto.Substring(caracteres); e.HasMorePages = !string.IsNullOrWhiteSpace(_texto); } }
' VB.NET Private Sub ImprimirButton_Click(sender As Object, e As EventArgs) Handles ImprimirButton.Click Using Pd = New System.Drawing.Printing.PrintDocument() Pd.PrinterSettings.PrinterName = ImpressoraComboBox.SelectedItem.ToString() AddHandler Pd.PrintPage, AddressOf Pd_PrintPage Texto = ImpressaoTextBox.Text Pd.Print() End Using End Sub Private Texto As String Private Sub Pd_PrintPage(sender As Object, e As System.Drawing.Printing.PrintPageEventArgs) Using Font = New Font("Times New Roman", 14) Using Brush = New SolidBrush(Color.Black) Dim Caracteres As Integer = 0 Dim Linhas As Integer = 0 e.Graphics.MeasureString(Texto, Font, New Size(e.MarginBounds.Width, e.MarginBounds.Height - Font.Height + 1), StringFormat.GenericTypographic, Caracteres, Linhas) e.Graphics.DrawString(Texto.Substring(0, Caracteres), Font, Brush, e.MarginBounds) Texto = Texto.Substring(Caracteres) e.HasMorePages = Not String.IsNullOrWhiteSpace(Texto) End Using End Using End Sub
Baixe o projeto de exemplo
Para baixar o projeto de exemplo desse artigo, assine a minha newsletter. Ao fazer isso, além de ter acesso ao projeto, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário no final do artigo.
Concluindo
Imprimir informações direto na impressora com C# e VB.NET não é uma tarefa muito difícil. Através da classe “PrintDocument” nós conseguimos imprimir figuras geométricas, imagens e texto diretamente na impressora, sem necessitar de uma janela de “preview“. No vídeo de hoje você aprendeu a listar as impressoras instaladas no sistema operacional e a utilizar a classe “PrintDocument” para imprimirmos texto em uma única página e em múltiplas páginas.
E você, já precisou imprimir informações direto na impressora? Utilizou a classe “PrintDocument“? Como é que foi a sua experiência? Conte-nos mais detalhes na caixa de comentários abaixo!
Até a próxima!
André Lima
Photo by Peter Shanks used under Creative Commons
https://pixabay.com/en/startup-start-up-notebooks-creative-593327/
Song Rocket Power Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/
Introdução ao Dapper Enviando SMS com C# e VB.NET
That’s great!
Previously in order to print a receipt in my application I used to make an RDLC report and print it directly by rendering to an Image!
Of coarse it wasn’t very good for performance and I face another issue when printing from computers with different screen resolutions, the receipt gets cropped or too small :(
I used PrintDocument for printing barcodes and had some issues adjusting the text since it often had Arabic text, one draw back was I always used PrintDocument as a component in a form, so I had relatively a lot of duplicate code, since I used a PrintDocument for each form where I need to print barcodes.
Seeing here that it can be used independently from a form is a great help, now I can extract all the logic to a helper class and used it instead of the duplicate code!
Thanks very much.
Hi Ahmed!
Yes, it is possible to instantiate a PrintDocument independently of the Form.. This way, as you said, you could easily join all your logic in a helper class, instead of duplicating it in every Form you need..
I’m glad I could help.. Let me know if you face difficulties adjusting your code..
Regards,
André Lima
Olá, André.
Cara, parabéns novamente! Mais um excelente post!
Agora, uma pergunta: com este método é possível fazer impressões com quadros? Digo, conseguir formatar uma impressão como se fosse um relatório montado no Report Viewer ou no Crystal Reports?
Abraços!
Olá André, muito obrigado pelo comentário!
Desculpe, mas não entendi direito a sua dúvida.. O que seria impressões com quadros? O PrintDocument é extremamente flexível, você pode imprimir qualquer informação em qualquer lugar da página.. Porém, por causa de toda essa flexibilidade, ele às vezes acaba sendo difícil de utilizar porque você tem que posicionar tudo “na mão”..
Abraço!
André Lima
Bom professor mais eu não consigo ver como baixar seus projetos
Olá Deuzivaldo!
Enviei o link no seu e-mail.. Qualquer coisa é só entrar em contato..
Abraço!
André Lima
Prezado,
Tenho um sistema WEB C# e preciso imprimir em uma impressora não fiscal , local. a solução é web e tem 3 impressoras de despacho onde sai um cupom para os entregadores levarem a entrega.
Voce teria algum post a respeito ou video de ex. ou este caso se aplica tambem pra este caso?
Olá!
Infelizmente eu não teria conteúdo sobre esse assunto, principalmente porque eu tenho pouquíssima experiência com desenvolvimento web.. Além disso, não tenho certeza se esse cenário que você está descrevendo seria implementável, uma vez que em aplicações web você tem diversas limitações quando ao lado do cliente, e não seria possível especificar uma impressora fixa que deveria ser utilizada para a impressão (que acredito ser o seu caso)..
O que eu costumo ver nesse caso é a criação de um serviço que fará a impressão e mandará para a impressora.. Aí a sua aplicação web simplesmente mandaria o job de impressão para esse serviço, ao invés de tentar imprimir direto pelo browser..
Mas, como falei anteriormente, não tenho experiência nessa área, aí fica difícil dar uma sugestão para resolver esse problema..
Abraço!
André Lima
Não consegui enviar arquivo em PDF direto para impressora ,como você mostrou enviando texto, o que tem que ser feito de diferente
Olá Wellington!
Você poderia dar mais detalhes? Qual das opções você testou e qual foi o resultado? Como ficou o seu código? Enfim, me explica melhor as dificuldades que você está tendo para eu conseguir te ajudar..
Abraço!
André Lima
Tenho um aplicativo que gera o Danfe da Nfe em PDF e quero enviar o mesmo direto para impressora padrão do Windows sem abrir o Preview do Windows.
Olá Wellington!
No seu primeiro comentário (que você pode acessar através deste link) eu te passei três links sobre impressão direta de PDF.. Você chegou a dar uma olhada neles?
Abraço!
André Lima
Não sei qual opção vou usar pois já testei outros por exemplo e.Graphics.DrawStrin para imprimir texto, essa e.Graphics.DrawImage para imprimir imagem , mas para imprimir PDF qual opção eu devo vou usar.
Olá Wellington!
Como mencionei no meu último comentário, a impressão de PDF direto na impressora é feita de outra maneira completamente diferente.. Não tem nada a ver com o Graphics..
Por favor, dê uma olhada nos links que eu mencionei e veja se eles te ajudam a resolver o seu problema..
Abraço!
André Lima
Essa e minha dificuldade já olhei os outros links fiz vários testes mas não tive sucesso na impressão do PDF.
Mas agradeço pelo atenção e muito obrigado.
Olá Wellington!
Quais testes você fez exatamente? Quais foram os resultados? Acabei de testar com o código apresentado em um dos links que eu te mandei e ele funcionou perfeitamente:
Abraço!
André Lima
Olá André, tudo bem?
Tenho aprendido muito vendo seus posts, você esta de parabéns!
Tenho apenas uma dúvida, existe alguma maneira de salvar em PDF o conteúdo do arquivo que eu imprimi diretamente?
Por exemplo: algum método que além de enviar para impressora padrão(impressora térmica), após a impressão salve o arquivo em PDF?
Desde já agradeço pela sua atenção.
Olá Júnior!
O mais fácil nesse caso seria trabalhar com alguma impressora virtual que gere PDFs.. Aí você poderia imprimir duas vezes (uma vez na impressora térmica e outra vez na impressora PDF).. O problema dessa solução é que você precisa instalar essa impressora virtual no computador dos clientes..
Talvez uma outra opção seria gerar um arquivo de imagem com a representação de cada página, aí você criaria um arquivo PDF com alguma biblioteca geradora de PDFs (já falei sobre esse tipo de biblioteca neste outro artigo), adicionando uma imagem em cada página.. O problema nesse caso é que tudo ficaria como imagem no PDF, mesmo que você tenha criado como texto no PrintDocument.. Isso fará com que você perca a possibilidade de pesquisar por texto dentro dos arquivos PDF gerados..
Parece que neste link tem um exemplo nessa linha com o gerador de PDF da SyncFusion:
How to generate PDF from Print Document?
Abraço!
André Lima
Olá André!
Estou desenvolvendo uma aplicação ASP.NET e preciso imprimir arquivos na impressora instalada na máquina client. Os exemplos que achei só funcionam se a impressora estiver cadastrada/instalada no servidor. Você teria algum exemplo de como fazer funcionar?
Olá José!
Infelizmente eu tenho pouquíssima experiência com desenvolvimento web, portanto, não saberei como te ajudar dessa vez.. Eu sugiro que você poste essa dúvida em um fórum especializado em ASP.NET (como o fórum de ASP.NET da MSDN) ou em alguma comunidade de ASP.NET no Facebook (como esta aqui)..
Abraço!
André Lima
Ola, preciso imprimir um arquivo pdf sem abrir o adobe, nem o printdialog. Sabe como fazer? ja utilizei esse site (https://vishalsbsinha.wordpress.com/2014/05/06/how-to-programmatically-c-net-print-a-pdf-file-directly-to-the-printer/) mas nao funcionou 100%. Ele mandou o arquivo para a impressora, ela piscou como se estivesse imprimindo, mas nao imprimiu nada, e quando mando para “cutepdf writer” ele funciona normal…
Olá Vinicius!
Várias pessoas já me perguntaram como fazer isso, mas infelizmente eu nunca tive tempo de escrever sobre esse tema.. Eu nunca fiz isso, mas quando outras pessoas me perguntaram, eu dei uma pesquisada e encontrei estes links (além desse que você mandou no seu comentário):
How can I send a file document to the printer and have it print?
How to print a PDF with C#
Talvez te ajude.. Depois conta aqui pra gente se você conseguiu resolver..
Abraço!
André Lima
Olá André.
Eu sou programador PHP e possuo um sistema que roda no browser do cliente, não entendo quase nada de C#, mas estou conseguindo imprimir direto na impressora seguindo teu exemplo. Mas eu tenho um misero probleminha que está me quebrando a cabeça…
Estou montando um app que faz requisições para meu servidor, informado dados do usuário e então, sempre que houver impressão pendentes, o sistema envia por JSON para o programa em C#, essa é a parte mais complicada de todas e eu consegui finalizar.
O que está me complicando aqui, é que não estou conseguindo automatizar o processo de impressão sem pressionar o botão, criei uma função, alterei o nome da função de clique no botão, etc…
eu consigo disparar a função de imprimir com seu código dentro, mas ele não funciona, só funciona apertando no botão mesmo…
ESTOU TROCANDO ESSE TRECHO:
private void imprimirButton_Click(object sender, EventArgs e)
POR ESTE:
private void Imprimir()
e posteriormente chamando a função de imprimir, mas não acontece nada…
durante a compilação não há erros, eu recebo um erro estranho somente quando dou o comando de imprimir pelo meu servidor (equivalente a chamar a função Imprimir()).
O erro no debugger é: Exceção gerada: ‘System.InvalidOperationException’ em System.Windows.Forms.dll
Poderia me ajudar a rodar seu codigo, bem como está no post, mas trocando o botão de “IMPRIMIR” por uma função que eu consiga chamar dentro do código?
Olá Ederson!
Só para confirmar antes, a sua aplicação é desktop, correto? Esse código só funcionará em projetos desktop..
De qualquer forma, não faço ideia do que possa estar acontecendo.. O código apresentado no artigo deveria funcionar sem a interação do botão, desde que você configure corretamente o nome da impressora, obviamente..
Como é que ficou o seu código exatamente?
Abraço!
André Lima
Ola!
Preciso imprimir descricao de produtos e seus valores porem precisa esta formatado na impressao, como posso fazer esse controle e junto com ele o controle de paginas preciso que sai de acordo com a quantidade de texto .
Por exemplo tenho 2000 produtos preciso que sai um em baixo do outro com seus valores na frente e que seja criado a quantidade de paginas necessarias para emissao desses produtos
Olá Luan!
Cara, imprimir isso manualmente com o PrintDocument vai ser bem difícil.. Eu recomendo que você utilize uma ferramenta de relatórios para te auxiliar nisso.. Dê uma olhada no Report Viewer ou Crystal Reports.. Ambos são gratuitos e eu tenho diversos tutoriais sobre essas ferramentas aqui mesmo no site.. Abra a categoria “Relatórios” e você vai encontrar muito conteúdo lá..
Abraço!
André Lima
André, esse código serve para imprimir em impressora térmica (de bobina 58mm) que conste na “lista de impressoras instaladas” (impressora que conste no painel de controle)?
Olá Leonardo!
Teoricamente sim.. Tenta aplicar o código aí e me avisa se funcionou.. Tem bastante gente que pergunta sobre impressora térmica.. O problema é que eu não tenho uma para conseguir testar..
Abraço!
André Lima