27 09 2017
Imprimindo múltiplas vias no Report Viewer
Alguns tipos de relatórios precisam ser impressos em múltiplas vias. Deixar a impressão das vias adicionais a cargo do usuário é um tiro no pé. Muito provavelmente ele esquecerá de configurar que o relatório deve ser impresso duas vezes e, todas as vezes que ele se esquecer disso, ele xingará mentalmente a empresa (ou desenvolvedor) que implementou aquele software sem a opção de imprimir múltiplas vias automaticamente.
Infelizmente não temos uma opção nativa que podemos configurar no Report Viewer de forma que ele imprima múltiplas vias do mesmo relatório. Porém, esse problema pode ser resolvido com algumas alterações na aplicação. No vídeo de hoje eu mostro para você três opções que podemos utilizar nos nossos projetos para imprimirmos múltiplas vias no Report Viewer:
Opção 1: Adicionando uma coluna “Via” no DataSet
Se você realmente tiver que exibir as duas vias no controle visualizador do Report Viewer (antes de realizar a impressão), ou se a primeira via for sutilmente diferente da segunda, aí não temos outra opção. Teremos que criar uma nova coluna no DataSet para representar o número da via:
Em seguida, nós temos que duplicar os dados da tabela que está alimentando o relatório (ou triplicar, quadruplicar, etc. – depende da quantidade de vias necessárias). No vídeo eu mostro um método que eu implementei para fazer a duplicação dos dados. Esse método recebe a DataTable original e retorna uma DataTable com “N” cópias das linhas:
// C# private DataTable GerenciarVias(DataTable origem, string nomeColunaVia, int qtdVias) { var retorno = origem.Copy(); for (var c = 2; c <= qtdVias; c++) { foreach (DataRow linha in origem.Rows) { var novaLinha = retorno.NewRow(); novaLinha.ItemArray = (object[])linha.ItemArray.Clone(); novaLinha[nomeColunaVia] = c; retorno.Rows.Add(novaLinha); } } return retorno; }
' VB.NET Private Function GerenciarVias(Origem As DataTable, NomeColunaVia As String, QtdVias As Integer) As DataTable Dim Retorno = Origem.Copy() For C = 2 To QtdVias For Each Linha As DataRow In Origem.Rows Dim NovaLinha = Retorno.NewRow() NovaLinha.ItemArray = DirectCast(Linha.ItemArray.Clone(), Object()) NovaLinha(NomeColunaVia) = C Retorno.Rows.Add(NovaLinha) Next Next Return Retorno End Function
Com esse método à nossa disposição (você pode colocá-lo em uma classe estática ou biblioteca de forma que ele possa ser utilizado em múltiplos lugares), basta chamarmos passando a DataTable original e a quantidade de vias desejada. Por fim, nós trocamos a DataSource do relatório, de forma que ele utilize essa nova DataTable com os dados duplicados:
// C# var novaTabela = GerenciarVias(this.DataSetRelatorio.Produto, "Via", 2); this.reportViewer1.LocalReport.DataSources.Clear(); this.reportViewer1.LocalReport.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("DataSetRelatorio", novaTabela)); this.reportViewer1.RefreshReport();
' VB.NET Dim NovaTabela = GerenciarVias(Me.DataSetRelatorio.Produto, "Via", 2) Me.ReportViewer1.LocalReport.DataSources.Clear() Me.ReportViewer1.LocalReport.DataSources.Add(New Microsoft.Reporting.WinForms.ReportDataSource("DataSetRelatorio", NovaTabela)) Me.ReportViewer1.RefreshReport()
Uma vez alterado o código da nossa aplicação, nós temos que ajustar também o nosso relatório. A primeira coisa que vamos fazer é dar um “refresh” nos campos do relatório, a fim de que o novo campo “Via” seja reconhecido:
Nota: caso você esteja tendo algum problema nessa etapa e o campo “Via” não esteja aparecendo, você pode utilizar o Report Viewer DataSet Editor para adicionar esse campo no seu relatório manualmente.
Em seguida, adicionamos um agrupamento “pai” no relatório, considerando essa nova coluna “Via” como expressão de agrupamento:
Como nós não precisaremos da informação do número da via no Tablix, nós podemos deletar a coluna que foi criada. Só tome cuidado para excluir apenas a coluna, e não o agrupamento todo!
Uma última coisa que temos que configurar é a quebra de página entre as instâncias do agrupamento. Dessa forma, cada via do relatório será impressa em uma página diferente:
Pronto! Com essas alterações o relatório será gerado “N” vezes, dependendo da quantidade de vias que configuramos na chamada do método “GerenciarVias“.
Opção 2: Configurando a quantidade de cópias no evento “Print”
Uma segunda sistemática que podemos utilizar para imprimirmos múltiplas vias no Report Viewer é alterarmos automaticamente a quantidade de vias que serão impressas quando o usuário clicar no botão “imprimir“. Essa opção pode ser utilizada quando você quiser imprimir duas (ou mais) vias idênticas do relatório sem necessariamente exibi-las no controle visualizador do Report Viewer.
Para implementarmos essa funcionalidade, temos que manipular o evento “Print” do controle do Report Viewer. Dentro desse evento, nós configuramos o número de cópias que queremos imprimir:
// C# private void reportViewer1_Print(object sender, Microsoft.Reporting.WinForms.ReportPrintEventArgs e) { e.PrinterSettings.Copies = 2; }
' VB.NET Private Sub ReportViewer1_Print(sender As Object, e As Microsoft.Reporting.WinForms.ReportPrintEventArgs) Handles ReportViewer1.Print e.PrinterSettings.Copies = 2 End Sub
Com essa alteração, quando o usuário clicar em “imprimir“, a quantidade de cópias será preenchida automaticamente:
Opção 3: Configurando a quantidade de cópias no PrintDocument
Por fim, uma última opção que podemos utilizar caso estivermos imprimindo o relatório sem o controle visualizador do Report Viewer (impressão direta) seria configurarmos o número de cópias no PrintDocument. Se utilizarmos o código apresentado no meu artigo sobre impressão direta com o Report Viewer, basta configurarmos a quantidade de cópias para o PrintDocument no método “Imprimir“.
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
Como você pode observar nesse vídeo, apesar de não termos uma opção nativa para imprimirmos múltiplas vias no Report Viewer, isso pode ser facilmente implementado de três maneiras. Ou nós adicionamos um campo para controlar o número da via no DataSet e agrupamos o relatório por esse campo, ou nós configuramos automaticamente o número de cópias a serem impressas no controle do Report Viewer ou nós configuramos essa mesma opção no PrintDocument, caso estejamos imprimindo o relatório direto na impressora.
Você já teve algum cenário na sua aplicação em que você precisou gerar múltiplas vias do mesmo relatório? Como é que você implementou essa funcionalidade? Utilizando uma das três maneiras indicadas nesse artigo ou de alguma outra forma? Aguardo ansiosamente os seus comentários no final do post!
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/
Aplicações Android com Xamarin – Parte 5 de N – Navegação entre telas Introdução ao Dapper
That’s really great!
Before, I used to make a different report for each case and use the ReportViewer.LocalReport.ReportEmbeddedResource to change between them ! :(
Thanks Very Much
Hi Ahmed!
Wow, that’s a lot o work.. :(
I’m glad I could help.. If you get any questions when you try to apply this technique, just let me know..
Regards,
André Lima
Thanks very much,Sir.
Bom dia professo muito bom eu vou usar para gera minhas ordem de serviços obrigados
Legal, Deuzivaldo! Qualquer dúvida que você ficar, é só entrar em contato..
Abraço!
André Lima
Hello Mr.Andre,
I hope you have been fine,
I ran into a problem with reports that need to have several variations which I couldn’t get to solve.
The problem is as follows :
Suppose a store has 10 branches
the required reports should display the items’ balances in one or more store,
in other words the number of columns in the table should vary dynamically according to the selected stores,
If the user selects 2 stores the columns should be :
Item ID , Item Name , Balance in Store 01 , Balance in store 02
If the user selects more or less stores the columns are added or removed
How is it possible to achieve that ?
Thanks very much,
I wish you the best, always.
Hi Ahmed! Here is everything OK, I hope you are well too..
To solve this problem, in the DataSet I would put the store information in rows (instead of each store being a column).. Example:
ItemID, Item Name, Store Name, Balance
Then, I would use a matrix component in the report, adding a column group by the “Store Name” field.. This way, the report would dynamically create one column for each store, depending on how many stores exist in the DataSet..
Are you familiar with the “column group” concept in the matrix component? If not, I recommend that you take a look in this article from the GotReportViewer website (which I am linking from the Web Archive, because it looks like the website is offline):
Designing matrix reports
Regards,
André Lima
Thanks very much sir,
works like a charm :)
You’re welcome, Ahmed! Happy it worked.. :)
Regards,
André Lima
Olá André bom dia, gostaria de saber como faço para o report imprimir em impressora de cupom fiscal, como eu configuro ele para quando imprimir a ultima linha ele para de puxar o papel.
Desde já agradeço
Olá Katiuce!
Eu não tenho certeza (infelizmente não tenho uma impressora fiscal para fazer esses testes), mas se você deixar o relatório sem rodapé e sem margem inferior, a impressão deveria concluir quando a última linha fosse impressa (e consequentemente a impressora deveria parar de puxar o papel)..
Como é que está a questão de rodapé e margem inferior no seu relatório?
Abraço!
André Lima
Amigo, como faço seu primeiro procedimento se eu trago as informações do banco de dados, por meio de Stored Procedure, alimento meu relatório com dados do banco,,,, ensina a fazer isso com dados reais
Olá André!
Vou repetir aqui o que eu te respondi via e-mail:
A lógica é exatamente a mesma.. No seu código, você está preenchendo a DataTable “prysmaDataSet.SP_SELECT_PARCELAMENTO” com os dados retornados da stored procedure.. Depois do método “Fill”, você só precisa fazer a mesma coisa que eu fiz no vídeo/artigo (chamar o método “GerenciarVias” passando essa DataTable)..
Abraço!
André Lima