Um fato que não consigo entender com facilidade é que, todos os meses, o post mais lido do meu blog sempre acaba sendo um post de 2010 sobre agrupamentos simples no Report Viewer do Visual Studio 2010. Levando em conta essa estatística, achei pertinente abordar de vez em quando o assunto “relatórios” em posts aqui no blog. Portanto, hoje quero falar um pouco sobre o suporte ao Crystal Reports no Visual Studio 2013.
Muitos não sabem, mas, desde a primeira versão do Visual Studio até o Visual Studio 2008, o Crystal Reports vinha integrado ao produto. A partir do Visual Studio 2010, o Crystal Reports passou a ser distribuído para usuários do Visual Studio como um download separado. Esse download é disponibilizado hoje em dia pela empresa que é a atual mantenedora do Crystal Reports, a SAP.
Até pouco tempo atrás (final de Junho de 2014), não existia suporte ao Crystal Reports no Visual Studio 2013. Só era possível trabalhar com Crystal Reports no Visual Studio 2012 ou anterior. Com o Support Pack 9 do Crystal Reports para Visual Studio, o suporte ao Visual Studio 2013 foi adicionado. Na data de escrita desse post, a versão atual era o Support Pack 10.
Adicionando o Crystal Reports ao Visual Studio 2013
Para instalar o Crystal Reports no Visual Studio, basta ir até a página de downloads da SAP e baixar o instalador. Infelizmente, a edição Express do Visual Studio não é suportada, ou seja, você precisa ter o Visual Studio Professional ou superior para se beneficiar do Crystal Reports. É importante notar que, na máquina do desenvolvedor deve ser instalado o “Install Executable“, e nas máquinas clientes deve ser instalado o “MSI 32 Bit” ou “MSI 64 Bit“, dependendo da arquitetura do processador:
Criando relatórios com o Crystal Reports do Visual Studio 2013
Após instalar o “Install Executable” na máquina do desenvolvedor com o Visual Studio 2013, você verá que uma nova opção chamada “Crystal Reports” aparece na categoria “Reporting” da tela “Add new Item“:
Ao adicionar um relatório do Crystal Reports, você pode escolher entre as opções de utilizar o Wizard, criar um relatório em branco ou abrir um relatório existente para editá-lo:
O designer do Crystal Reports é exatamente o mesmo desde as versões mais antigas:
Utilizando o Crystal Report Viewer no Windows Forms
Utilizar o viewer do Crystal Reports no Windows Forms é extremamente fácil, uma vez que o componente é instalado automaticamente na Toolbox do Visual Studio:
Utilizando o Crystal Report Viewer no WPF
Já no WPF, o processo é um pouco mais complicado. Curiosamente, o componente não é adicionado automaticamente na Toolbox do Visual Studio, portanto temos que adicioná-lo manualmente. Para fazer isso, basta clicar com o botão direito do mouse na categoria “General” na Toolbox do Visual Studio e escolher a opção “Choose Items“:
Na tela “Choose Toolbox Items“, vá até a aba “WPF Components“, procure pelo item “CrystalReportsViewer” e marque a caixa de opções para que esse item seja adicionado à Toolbox:
Feito isso, você só precisa arrastá-lo diretamente para dentro do bloco XAML onde você quer que o viewer seja adicionado:
Além do Report Viewer da própria Microsoft, o Crystal Reports é uma excelente opção para a geração de relatórios com o Visual Studio, caso você tenha a edição Professional ou superior. Agora que já sabemos como adicioná-lo ao Visual Studio, no futuro escreverei ocasionalmente sobre esse tema também.
Se você quer ficar por dentro das novidades do meu site, inscreva-se e receba um e-mail todas as sextas-feiras onde falo sobre os posts da semana, o tema da próxima semana e também um pouquinho da minha vida pessoal quando pertinente.
O autor da questão no fórum da MSDN está utilizando um banco de dados Access. Para deixar o cenário igual ao da questão, vou também utilizar um banco de dados Access. Porém, essa dica funciona com qualquer outro banco de dados que você esteja utilizando.
Passo 1 – Criando um banco de dados Access com o caminho das imagens
Primeiramente, vamos criar um banco de dados no Access. Para isso, abra o Microsoft Access e escolha a opção “New Blank Database“. Salve esse banco de dados em algum lugar no seu disco. Eu salvei o meu em C:\ReportViewer\DB.mdb:
Quando o Access cria o banco de dados, ele já vem com um “Table1” criado. Altere para o modo de design clicando em “View” => “Design View“, e, quando o Access perguntar o nome que queremos utilizar para a tabela, escolha o nome “Exemplo“:
Na tabela exemplo, crie três campos: ID (primary key, auto-inc), Descricao (Short Text) e CaminhoImagem (Long Text):
Volte novamente para o “Datasheet View” e insira três registros na tabela “Exemplo“, com os caminhos para a imagem devidamente preenchidos.
Eu coloquei três imagens de exemplo no diretório C:\ReportViewer. Você pode baixa-las nos seguintes links: img1.png, img2.png, img3.png.
Passo 2 – Criando o relatório no Report Viewer
Agora que já criamos o banco de dados Access, vamos criar o relatório no Report Viewer. Crie um novo projeto do tipo “Windows Forms Application” e adicione um novo item do tipo “Report” a esse projeto e dê o nome de “ReportExemplo“:
O próximo passo é adicionar o DataSet que vai alimentar o nosso relatório. Para fazer isso, vá até a área chamada “Report Data” e clique em “New” => “Dataset“:
Na tela “Choose a Data Source Type” escolha “Database“, e na tela “Choose a Database Model” escolha “Dataset“:
A próxima tela do wizard é a “Choose Your Data Connection“. Nessa tela devemos criar a conexão com o nosso banco de dados do Microsoft Access. Para isso, clique em “New Connection“:
Configure a conexão com o banco de dados escolhendo o tipo “Microsoft Access Database File” e selecionando o caminho onde o seu banco de dados está armazenado (no meu caso C:\ReportViewer\DB.mdb):
Ao ser questionado se você quer criar uma cópia do banco de dados no seu projeto, escolha a opção “Não“. Dessa forma, a conexão irá apontar diretamente para o banco de dados onde ele está originalmente armazenado (novamente, no meu caso C:\ReportViewer\DB.mdb):
Dê um nome qualquer para a conexão (eu escolhi “DBConnectionString“, que o próprio Visual Studio sugeriu) e na tela “Choose Your Database Objects“, selecione a tabela que alimentará o relatório (no caso do nosso exemplo é a tabela “Exemplo“):
Finalmente, na tela chamada “Dataset Properties“, dê um nome para o Dataset que está sendo criado no relatório (eu escolhi “DBDataSet“) e clique eu OK:
Agora que já adicionamos o DataSet que alimentará o relatório, vamos adicionar o objeto que exibirá esses dados. Vá até a barra de ferramentas e adicione um componente “Table” ao relatório e configure-o da seguinte maneira:
Note que na terceira coluna, no campo que exibirá a imagem, eu adicionei um controle “Image“. Ao adicionar essa imagem ao relatório, o ReportViewer vai exibir a tela de propriedades da imagem. Nessa tela é onde devemos configurar o caminho onde as imagens estão armazenadas. Para que o exemplo funcione corretamente, na tela de propriedades da imagem, configure a “image source” como “External” (uma vez que as imagens serão carregadas de um caminho em disco) e clique no botão “fx” em “Use this image” para configurar o caminho das imagens:
Na tela de configuração da expressão, configure-a da seguinte forma:
=”File://” & Fields!CaminhoImagem.Value
Dessa forma nós acabamos de configurar o relatório para que a imagem seja exibida a partir de um caminho em disco. Simples, não? Agora vamos ajustar o nosso projeto Windows Forms para que esse relatório seja exibido corretamente.
Passo 3 – Exibindo o relatório com o controle do Report Viewer
O último passo desse tutorial mostra como configurarmos corretamente o controle do Report Viewer para exibir o relatório que acabamos de criar, de forma que as imagens sejam realmente exibidas, uma vez que com as configurações padrão, as imagens não seriam exibidas (porque o ReportViewer bloqueia imagens externas por padrão).
Vá até o Form1 criado pelo Visual Studio e adicione um controle do visualizador do Report Viewer. Ele está localizado na barra de ferramentas, dentro da categoria “Reporting“:
Na smart tag do controle de relatórios, abra o ComboBox chamado “Choose Report” e escolha o relatório criado anteriormente. Além disso, escolha a opção “Dock in Parent Container” para que o controle ocupe todo o espaço do Form1:
Agora vá até o code-behind do Form1 e, no código adicionado pelo Report Viewer no event handler “Form1_Load“, adicione a linha para que o controle do Report Viewer permita a exibição de imagens externas:
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'DBDataSet.Exemplo' table. You can move, or remove it, as needed.
this.ExemploTableAdapter.Fill(this.DBDataSet.Exemplo);
this.reportViewer1.LocalReport.EnableExternalImages = true;
this.reportViewer1.RefreshReport();
}
Pronto! Agora o tutorial está finalmente pronto! Execute e aplicação e confira que as imagens são exibidas corretamente:
Concluindo
É muito fácil exibir imagens a partir de um caminho em disco no ReportViewer. Os principais detalhes que você deve prestar atenção são:
Configurar a propriedade “Image Source” do controle imagem como “External”
Configurar o caminho da imagem corretamente
Configurar a propriedade “EnableExternalImages” como “true” no controle de exibição do Report Viewer
Para ficar por dentro dos últimos artigos do meu blog, receber dicas que eu não publico em nenhum outro lugar e além disso receber gratuitamente uma lista com quase vinte recursos sobre o ReportViewer, assine a minha newsletter.
Hoje eu recebi uma dúvida de um inscrito sobre numeração de páginas frente e verso no Crystal Reports. Ao invés de simplesmente respondê-lo, resolvi escrever um post rápido detalhando a solução.
Veja a questão (editada para fins de publicação):
André bom dia,
Estou fazendo um relatório em Crystal Reports e o cliente quer que os números de páginas saiam da seguinte forma: “Pag. 1″ e a próxima “Pag. 1 verso” e assim sucessivamente, pois ele vai imprimir o relatório em frente e verso, creio eu que um loop no crystal resolveria isto, mas não tenho muita intimidade com o Crystal e não sei como fazer este loop, se fosse no desenvolvimento eu faria tranquilamente.
Se puder me ajudar te agradecerei muito
Claro que eu posso ajudar. A maneira como eu pensei em solucionar essa questão foi utilizando uma simples fórmula. Com a página atual real (função “PageNumber” no Crystal Reports), podemos detectar se estamos em uma página ímpar (páginas “frente“) ou em uma página par (páginas “verso“). Dependendo do resultado se a página for par ou ímpar, utilizamos “Pag. X” ou “Pag. X verso“. Veja a tabela abaixo:
Note que a fórmula para descobrir o número final da página é diferente dependendo da página ser par ou ímpar. Para páginas ímpares (páginas “frente“), utilizamos a fórmula: (“Página Real” + 1) / 2. Para páginas pares (páginas “verso“), utilizamos a fórmula: “Página Real” / 2.
Na linguagem de fórmulas do Crystal Reports, para descobrirmos se um número é ímpar, basta utilizarmos o operador “mod” de 2. Esse operador retorna o resto da divisão de um número pelo outro (nesse caso o número “dois“). No caso do resto da divisão ser “1“, o número é ímpar. Caso contrário (resto zero), o número é par. Aí basta aplicar a fórmula para calcular o número final conforme vimos na tabela:
// Página ímpar
if (PageNumber mod 2) = 1 then
"Página " + CStr((PageNumber + 1) / 2, 0)
// Página par
else
"Página " + CStr(PageNumber / 2, 0) + " verso"
E com isso temos o resultado esperado. Na página 1, temos “Página 1″. Na página 2, temos “Página 1 verso”. E aí por diante.
É isso aí. Espero que essa dica ajude outras pessoas também. Como você pode perceber, eu costumo dar bastante atenção para os meus inscritos. Caso você também queira ficar por dentro das novidades do meu site, além de receber dicas que eu só compartilho por e-mail, assine a minha newsletter agora mesmo através desse link ou utilizando o formulário abaixo.
É fato que ao desenvolvermos relatórios é praticamente impossível não precisarmos de algum tipo de totalizador. Seja a soma de valores, contagem de registros ou média de um determinado range de valores, é muito improvável que um relatório não tenha a totalização de alguma propriedade. No artigo de hoje veremos como trabalhar com totalizadores no Report Viewer.
Antes de começar, caso você esteja trabalhando com o Visual Studio Express, saiba que não existe suporte ao desenvolvimento de novos relatórios nessa edição do Visual Studio. É possível adicionar o controle do Report Viewer para a exibição de relatórios existentes no Visual Studio Express, mas, para desenhar novos relatórios ou editar relatórios existentes, você precisaria utilizar o SQL Server 2012 Report Builder. Outra opção é migrar para o Visual Studio Community, que suporta o Report Viewer nativamente. Os exemplos desse artigo utilizarão a edição Community do Visual Studio.
Criando o relatório de exemplo
Para demonstrar a criação de totalizadores no Report Viewer, vamos criar um projeto do tipo “Windows Forms Application“. No Form que é criado automaticamente pelo Visual Studio, adicione o controle do Report Viewer e faça o “docking” do controle para que ele ocupe o espaço inteiro do Form.
Feito isso, vamos primeiramente criar o DataSet que será utilizado para alimentar os dados do relatório. Para isso, adicione um novo item do tipo “DataSet” ao projeto (“DataSet” fica dentro da categoria “Data” na tela “Add New Item“). Utilize o nome “DataSetRelatorio.xsd” para o novo DataSet a ser criado.
Após ter criado o DataSet, adicione uma nova DataTable chamada “Transacao” com as seguintes colunas: IDTransacao (auto incremento, chave primária), Descricao (string), Categoria (string), Valor (double).
Com o DataSet criado, vamos adicionar um relatório ao projeto. Para isso, utilize a opção “Add New Item” e escolha o item “Report“, que fica dentro da categoria “Reporting“. Dê o nome de “RelatorioTransacao.rdlc” ao relatório que será criado.
Uma vez executados esses passos, o Visual Studio terá criado um novo relatório em branco. Poderíamos ter utilizado o “Report Wizard“, mas, ele requer maiores explicações. Pretendo escrever um artigo somente sobre o “Report Wizard” no futuro.
No relatório em branco, a primeira tarefa que temos que executar é adicionar a fonte de dados para o relatório. Para isso, utilize a opção “New DataSet” da janela “Report Data“.
Na janela que se abre, preencha o nome do novo DataSet como “DataSetRelatorio“, escolha na caixa de opções “Data source” o DataSet que criamos anteriormente (“DataSetRelatorio“) e escolha na caixa de opções “Available datasets” a tabela “Transacao“.
Feito isso, adicione no relatório um controle do tipo “Table” e arraste as colunas do DataSet para dentro das células detalhe da tabela.
Como o nosso DataSet tem quatro colunas e o controle “Table” é criado com somente três colunas, precisamos adicionar uma coluna extra para o campo “Valor“. Insira uma nova coluna à direita de “Categoria” clicando com o botão direito e escolhendo a opção “Insert Column / Right“.
Finalmente, arraste o campo “Valor” até a célula da nova coluna que acabou de ser criada e formate o relatório para que ele fique com uma aparência mais profissional.
Adicionando os totalizadores
A maneira mais fácil de criarmos totalizadores é clicarmos com o botão direito sobre a coluna que queremos totalizar e escolhermos a opção “Add Total“.
Isso fará com que a soma dos valores da coluna escolhida seja exibida na última linha da tabela.
Note que um totalizador nada mais é do que uma expressão. Nesse caso, para adicionarmos uma soma de todos os valores da coluna “Valor“, utilizamos a expressão “Sum“, que recebe como parâmetro o nome da coluna a ser somada.
Entretanto, a expressão “Sum” não é a única expressão que podemos utilizar para criarmos totalizadores. Além dessa expressão, temos à nossa disposição outras diversas funções que podem ser utilizadas para agregarmos um conjunto valores. Você encontra uma lista dessas funções clicando com o botão direito em uma das outras células da linha inferior da tabela e escolhendo a opção “Expression“.
Na tela “Expression“, confira todas as funções de agregação dentro da categoria “Common Functions / Aggregate“.
As principais expressões utilizadas para agregações de valores são:
– Avg: retorna a média de um conjunto de valores – Count: retorna a contagem de itens de um conjunto de valores – CountDistinct: retorna a contagem de itens distintos de um conjunto de valores – Max: retorna o maior valor de um conjunto de valores – Min: retorna o menor valor de um conjunto de valores – Sum: retorna a soma de um conjunto de valores
Por exemplo, para adicionarmos a contagem de itens no nosso relatório, podemos utilizar a expressão “=Count(Fields!IDTransacao.Value)“.
Testando o relatório
A única maneira de testarmos um relatório local desenvolvido com o Report Viewer é utilizando-o dentro de uma aplicação. Portanto, volte até o Form criado anteriormente e escolha o relatório que acabamos de criar utilizando a “smart tag” do controle do Report Viewer.
Ao escolher o relatório, o Visual Studio automaticamente criará um DataSet e um BindingSource apontando para o DataSet que criamos anteriormente (“DataSetRelatorio“, tabela “Transacao“).
Em um cenário de aplicação normal nós provavelmente acessaríamos algum banco de dados para recuperar a lista de transações. Porém, a fim de simplificarmos esse exemplo, vamos adicionar algumas transações manualmente no DataSet do relatório. Para isso, vá até o code-behind do Form e adicione o seguinte código no construtor:
public FormTotalizadores()
{
InitializeComponent();
DataSetRelatorio.Transacao.AddTransacaoRow("Saldo anterior", "Crédito", 1234.56);
DataSetRelatorio.Transacao.AddTransacaoRow("Pagamento de Conta", "Débito", -125.98);
DataSetRelatorio.Transacao.AddTransacaoRow("Salário", "Crédito", 6324.34);
DataSetRelatorio.Transacao.AddTransacaoRow("Rendimentos", "Crédito", 243.56);
DataSetRelatorio.Transacao.AddTransacaoRow("Pagamento de Conta", "Débito", -230.99);
DataSetRelatorio.Transacao.AddTransacaoRow("Transferência", "Débito", -500.00);
DataSetRelatorio.Transacao.AddTransacaoRow("Pagamento de Conta", "Débito", -39.45);
DataSetRelatorio.Transacao.AddTransacaoRow("Depósito", "Crédito", 200.00);
DataSetRelatorio.Transacao.AddTransacaoRow("Pagamento de Conta", "Débito", -44.90);
DataSetRelatorio.Transacao.AddTransacaoRow("Débito automático", "Débito", -135.50);
}
Execute a aplicação e veja que a contagem de transações é exibida no final da coluna “Categoria” e a soma das transações é exibida no final da coluna “Valor“.
Totalizadores + Agrupamentos
Imagine a situação onde desejemos adicionar um agrupamento das transações por “Categoria“. Como o Report Viewer trata totalizadores quando temos agrupamentos no nosso relatório? Outras ferramentas de relatórios (como o Crystal Reports, por exemplo) demandam que você especifique explicitamente que o totalizador deve considerar o agrupamento. Felizmente a engine do Report Viewer é inteligente o suficiente para detectar se o totalizador está dentro ou fora de um grupo e, caso esteja dentro do grupo, ele automaticamente considerará que o totalizador deverá considerar somente os registros daquele grupo. Já no caso do totalizador estar fora de uma área de agrupamento, o Report Viewer considerará todos os registros para calcular o valor do totalizador.
Para entendermos essa sistemática, vamos agrupar as linhas da nossa lista de transações por “Categoria“. Caso você não saiba como fazer isso, confira o meu outro artigo sobre agrupamentos simples no Report Viewer. O resultado deve ficar parecido com as imagens a seguir.
Execute a aplicação e veja que o Report Viewer calcula os totalizadores justamente da forma que esperamos.
Caso você queira explicitamente indicar para o Report Viewer que o totalizador deve considerar somente os registros do grupo (não é necessário, mas, vai que você queira fazer isso), basta editar a expressão do totalizador e adicionar um segundo parâmetro com o nome do agrupamento. Por exemplo, a expressão para a somatória dos valores da transação pelo grupo de categorias ficaria a seguinte: “=Sum(Fields!Valor.Value, “Categoria”)”
Concluindo
Não importa o relatório que você for desenvolver, muito provavelmente ele terá algum tipo de totalizador, seja a contagem de registros, somatório de valores, média de valores, etc. Nesse artigo você aprendeu a adicionar totalizadores no Report Viewer, tanto em relatórios simples quanto em relatórios com agrupamentos. Para ficar por dentro dos próximos posts, assine a minha newsletter clicando aqui ou utilizando o formulário abaixo.
Em todo relatório que você desenvolver (ou tiver que dar manutenção) você provavelmente contará com alguma fórmula. No Report Viewer as fórmulas são chamadas de expressões (ou “expressions“). A documentação oficial sobre expressões no Report Viewer deixa um pouco a desejar, sem falar que não existe uma versão traduzida para o português. Pensando nisso, resolvi escrever esse artigo abordando as principais expressões que podemos utilizar em relatórios do Report Viewer.
Não tenho como comentar sobre todas as expressões disponíveis no Report Viewer, uma vez que elas são muitas. Porém, acredito que as expressões que listarei neste artigo são as mais comuns e com certeza algum dia você irá utilizá-las nos seus relatórios.
Inserindo expressões no relatório
Podemos utilizar expressões em praticamente todos os controles e propriedades dos relatórios no Report Viewer. Podemos inclusive utilizar expressões nas propriedades de visibilidade e cor de fundo para implementarmos formatação condicional.
Para adicionarmos expressões em células de uma tabela ou em um campo texto, basta clicarmos com o botão direito do mouse sobre o item desejado e escolhermos a opção “Expression“:
Já para utilizarmos expressões em propriedades, basta abrirmos a caixa de opções da propriedade e, caso ela suporte a utilização de expressões, você encontrará o item “Expression“. Por exemplo, a propriedade “Hidden” suporta expressões:
Agora vamos ver as principais expressões que podemos utilizar no Report Viewer.
Estruturas de decisão
Acredito que as expressões que implementam estruturas de decisão são as mais utilizadas no desenvolvimento de relatórios (em qualquer plataforma). Pense bem, quando desenvolvemos aplicativos, qual é a estrutura de controle mais utilizada? Muito provavelmente os “ifs“. Pois bem, o “if” é uma estrutura de decisão (ou controle de fluxo) que você pode utilizar também dentro dos seus relatórios do Report Viewer.
No Report Viewer, ao invés de simplesmente chamar-se “if“, essa estrutura de controle se chama “Iif” (vindo do Visual Basic, também presente no Excel, por exemplo). Sua utilização é muito simples. O primeiro parâmetro é uma condição, o segundo parâmetro é o valor que deve ser considerado caso a condição seja verdadeira e o terceiro parâmetro é o valor que deve ser considerado caso a condição seja falsa.
Por exemplo, para retornar 0 caso um valor seja negativo ou o próprio valor caso ele seja positivo, podemos utilizar a seguinte expressão:
A segunda estrutura de decisão é o “Switch“, que você provavelmente também já conhece da programação em C# ou VB.NET. Com ele conseguimos avaliar diversas condições e indicar o valor de retorno que deve ser utilizado para cada uma das opções. Por exemplo, se tivermos um campo chamado “IDCategoria” (com os itens “C“, “D“, “N“) e quisermos converter para um texto do tipo “Crédito“, “Débito” ou “Neutro“, podemos fazer da seguinte forma:
Estruturas de decisão são muito utilizadas para fazer formatações condicionais, uma vez que podemos retornar uma cor baseada em uma condição (ou até mesmo verdadeiro/falso para mostrar/esconder um item do relatório). Não vou abordar esse assunto neste artigo pois quero escrever sobre isso em um artigo futuro.
Totalizadores
Existe uma categoria de expressões que servem para totalizar grupos de valores no Report Viewer. Não vou detalhá-las nesse artigo pois semanas atrás eu escrevi um post justamente sobre totalizadores no Report Viewer.
Datas
Quer mostrar a data atual no seu relatório? Basta utilizar a expressão “Today“. Ou, se quiser pegar uma parte específica de uma data, utilize as expressões “Day“, “Month” e “Year” passando a data desejada. Exemplo:
Já se quisermos adicionar dias a uma data específica, podemos utilizar a expressão “DateAdd“. Essa expressão recebe o intervalo a ser utilizado (“Hour” para adicionar horas, “Day” para adicionar dias, e por aí vai) o valor para o intervalo e a data original. Por exemplo, para retornarmos a data atual + 30 dias, poderíamos utilizar a seguinte expressão:
=DateAdd(DateInterval.Day, 30, Today)
Formatação de strings
Além de ser possível concatenarmos strings através do operador “&“, também podemos formata-las e ajusta-las com algumas outras expressões. Por exemplo, para formatar uma string utilizamos a expressão “Format“. O primeiro parâmetro dessa expressão é o valor a ser formatado e o segundo parâmetro é o formato. Para formatarmos um campo de valor no formato de moeda, utilizamos a seguinte expressão:
=Format(Fields!Valor.Value, "C")
Também é possível formatarmos datas com essa expressão. Para uma lista completa de formatos disponíveis, confira a documentação.
Além da expressão “Format“, temos à nossa disposição a expressão “Right” que utilizamos para pegarmos os valores de uma string de trás pra frente (por exemplo, os três últimos caracteres de uma string), a expressão “Left” para pegarmos os valores à esquerda da string (por exemplo, os três primeiros caracteres de uma string), a expressão “Len” que utilizamos para descobrir a quantidade de caracteres de uma string e a expressão “InStr” que serve para descobrirmos a primeira posição em que um caractere específico aparece dentro de uma string.
Informações do relatório
Outro tipo de expressão muito útil são as expressões que nos retornam informações gerais do relatório. Por exemplo, podemos utilizar a expressão “ReportName” para descobrirmos o nome do relatório e a expressão “ExecutionTime” para descobrirmos a hora em que o relatório foi gerado.
Duas expressões que são muito utilizadas e se enquadram nessa categoria são as expressões “PageNumber” e “TotalPages“. Elas retornam o número da página atual e o número total de páginas, respectivamente. Portanto, para colocarmos no rodapé a informação “Página X de Y“, utilizamos a seguinte expressão:
="Página " & Globals.PageNumber & " de " & Globals.TotalPages
Porém, é importante notar que essas expressões se encontram dentro do grupo de expressões globais (por isso elas começam com “Globals.“). Esse tipo de expressão só pode ser utilizado no cabeçalho e rodapé do relatório, e não na área de detalhes. Para uma lista completa das expressões do grupo global, confira a documentação.
Além dessas expressões, temos também duas outras muito úteis, que são as expressões “First” e “Last“. Como o próprio nome diz, elas servem para retornar o primeiro e o último registro da fonte de dados.
Códigos customizados
As expressões abordadas até agora muitas vezes são o suficiente para gerarmos um relatório de qualidade. Porém, algumas vezes precisamos de lógicas mais complexas. Pensando nisso, a Microsoft possibilita a utilização de métodos customizados (estáticos e implementados em .NET) nas expressões do Report Viewer. O processo para utilizar códigos customizados é um pouco extenso, portanto não vou abordá-lo nesse artigo. Pretendo escrever um artigo somente sobre esse tema mais pra frente. Por enquanto, caso você precise dessa funcionalidade, confira a documentação.
Entretanto, algo que eu posso mostrar nessa seção é a utilização de métodos do namespace Microsoft.VisualBasic e a utilização das classes System.Convert e System.Math. Esse namespace e essas classes são referenciadas automaticamente pelo Report Viewer, portanto, é possível utilizá-las nas nossas expressões sem nenhum esforço. Por exemplo, suponha que você queira retornar o valor absoluto de um campo numérico (valor absoluto significa que sempre teremos um valor positivo, ou seja, se o valor original for negativo, ele perderá o sinal e virará positivo). Nesse caso, podemos utilizar o método Abs da classe Math:
=Math.Abs(Fields!Valor.Value)
Concluindo
A engine do Report Viewer provê várias expressões que fazem com que os nossos relatórios fiquem mais ricos. As expressões abordadas nesse artigo são as que eu mais utilizei nos relatórios que eu desenvolvi até hoje. Existem outras além dessas que você pode conferir na documentação (apesar da documentação do Report Viewer ser bem fraca, como eu mencionei anteriormente). É possível ainda criarmos código customizado em .NET e utilizarmos nas expressões do Report Viewer (isso não foi mostrado nesse artigo, mas, pretendo abordar no futuro). Com isso, espero que você consiga solucionar as demandas mais comuns para a geração de relatórios com o Report Viewer.
E você, já conhecia todas essas expressões? Tem alguma que você acha importante que eu deixei de fora? Dê a sua opinião nos comentários.
Finalmente, caso você ainda não tenha feito, convido para que você assine a minha newsletter. Ao inscrever-se, você sempre ficará por dentro de todos os novos artigos publicados aqui no site, além de ficar sabendo em primeira mão sobre o tema do próximo artigo e receber dicas extras que eu só compartilho por e-mail. Você pode assinar a minha newsletter através desse link ou utilizando o formulário logo abaixo.
Em aplicativos que contém algum tipo de catálogo de endereços, é muito comum termos a necessidade de imprimir etiquetas (para envelopes) com os nomes e endereços das pessoas desse catálogo. Para solucionar esse problema, o recomendado é que utilizemos alguma ferramenta de geração de relatórios. Se a sua ferramenta de geração de relatórios é o ReportViewer, é muito fácil implementar um relatório que imprima as etiquetas utilizando o conceito de colunas no ReportViewer. Esse é o tema do artigo de hoje.
Nos sistemas que eu trabalhei até hoje, eu nunca precisei imprimir etiquetas. Mas, acredito que deva ter sido pela natureza dos sistemas que eu trabalhei (por exemplo, sistemas relacionados com cargos e salários e sistemas relacionados ao ramo florestal). Em sistemas que possuem catálogo de endereços, é comum a necessidade de imprimirmos etiquetas.
Eu me atentei para essa realidade quando um dos inscritos da minha newsletter me questionou sobre a ordenação que o ReportViewer utiliza quando trabalhamos com o conceito de colunas (que é justamente como conseguimos imprimir etiquetas). Vou explicar melhor a dúvida desse inscrito mais adiante. A princípio, vamos criar um simples relatório para imprimirmos algumas etiquetas com o ReportViewer.
Criando o DataSet
Primeiramente, vamos criar um novo projeto do tipo “Windows Forms Application“. Nesse projeto, adicione um novo DataSet, que servirá de fonte de dados para o nosso relatório, ou seja, é nesse DataSet que teremos as informações das etiquetas que serão impressas. Como de costume, o item do tipo DataSet está presente dentro da categoria “Data”. Dê o nome de “DataSetEtiqueta” para o DataSet que será criado:
Dentro do DataSetEtiqueta, crie uma nova DataTable com o nome “Etiqueta” e os campos IDEtiqueta, Nome, Endereco, CEP, Cidade e Estado. Todos os campos devem ser do tipo string, exceto o campo IDEtiqueta, que deve ser do tipo auto incremento:
Note que nessa DataTable temos todos os atributos necessários para a geração das etiquetas. Provavelmente esses dados não estarão disponíveis no seu banco de dados exatamente dessa forma, mas, ao trabalharmos com relatórios, é sempre uma boa prática construirmos exatamente o DataSet necessário para o relatório e preenchermos esse DataSet com os dados vindos de outras tabelas do banco de dados.
Criando o relatório
Agora que temos o DataSet com a tabela “Etiqueta“, vamos criar um novo relatório do ReportViewer para exibirmos as informações dessas etiquetas. Adicione um item do tipo “Report” (que se encontra dentro da categoria “Reporting“), dando o nome de “ReportEtiqueta” para esse novo relatório:
Assim que o relatório tiver sido criado, vá até a página de propriedades, encontre a propriedade chamada “Columns” e altere o valor dessa propriedade para “2“:
Note que podemos também definir o espaçamento entre uma coluna e outra (o valor padrão é 0,13 cm). Normalmente, quando trabalhamos com etiquetas, é comum a utilização de folhas que já possuem as etiquetas de forma que possamos simplesmente retirá-las da folha e colá-las aonde quer que seja. Para que as etiquetas sejam impressas no lugar certo, você terá que configurar as propriedades “Columns” e “ColumnSpacing” de acordo com a folha de etiquetas que você estiver utilizando. Para esse exemplo, vou utilizar 2 colunas com o espaçamento padrão de 0,13 centímetros.
Quando você altera a propriedade “Columns“, note que o relatório apresentará a quantidade de colunas que você acabou de definir. No caso do nosso exemplo, conseguiremos visualizar duas colunas na área do relatório:
Você pode configurar o tamanho horizontal das colunas utilizando o “slider” (linha vertical cinza entre uma coluna e outra). Para ter uma noção precisa do tamanho de cada coluna, recomendo que você ative a régua do relatório através do menu de contexto:
Apesar de conseguirmos visualizar as duas colunas que configuramos anteriormente, só é possível adicionarmos controles na primeira coluna da esquerda. O ReportViewer fará automaticamente a quebra dos dados de uma coluna para a outra, seguindo primeiro na vertical (um registro abaixo do outro) e, quando a página tiver terminado, o ReportViewer começará a utilizar a segunda coluna, e assim por diante.
Uma vez configurado o layout inicial do relatório, adicione um controle do tipo “List” dentro da primeira coluna do relatório e configure o DataSet desse controle conforme a imagem a seguir:
Finalmente, arraste os campos do DataSet para dentro do controle “List” conforme podemos observar na imagem abaixo:
Para o campo relacionado às colunas “Cidade” e “Estado“, podemos criar um TextBox com uma expressão customizada (já que queremos mostrar “Cidade / Estado”). Veja como fica a fórmula para esse caso:
Além de configurar corretamente o tamanho das colunas e seu espaçamento, é importante que você configure também o tamanho da página e suas margens. Para isso, vá até o menu “Report” e escolha a opção “Report Properties“. Na janela “Report Properties“, configure o tamanho da página e margens (eu configurei um tamanho qualquer de forma que quatro etiquetas fossem exibidas por página):
Exibindo o relatório
Uma vez criado o relatório, temos que exibi-lo. Vá até o “Form1” criado automaticamente pelo Visual Studio e, de preferência, altere o seu nome para algo que faça mais sentido (como “FormEtiqueta“). Feito isso, adicione um controle do ReportViewer no formulário e configure-o para que ele ocupe a tela toda (“Dock in parent container” na “smart tag” do controle). Por fim, na “smart tag” do controle do ReportViewer, abra a caixa de opções “Choose Report” e escolha o relatório que criamos anteriormente (ReportEtiqueta):
Após ter selecionado o relatório, vá até o code-behind do formulário, encontre o método “Load” e adicione algumas linhas na tabela “Etiqueta” (obviamente, em situações reais, esses dados deveriam ser carregados de um banco de dados):
private void FormEtiqueta_Load(object sender, EventArgs e)
{
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("André Alves de Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW");
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Larissa Aldine Müller Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW");
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Sophie Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW");
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Fulano de Tal", "Praça da Matriz", "13480-000", "Limeira", "SP");
this.reportViewer.RefreshReport();
}
Execute a aplicação e veja o resultado:
Notou como ficou esquisito e as colunas nem foram utilizadas? Na realidade elas foram. O que acontece é que, por padrão, o ReportViewer exibe o preview em um modo que não é o de impressão (e que, na realidade, ninguém entende como funciona). Para ver o resultado final, você precisa alterar para o modo de impressão, clicando no botão “Print Layout“:
Aí sim o resultado fará muito mais sentido:
Caso você queira fazer com que o modo de impressão seja exibido por padrão, basta utilizar o método “SetDisplayMode” passando o “PrintLayout” antes da chamada de “RefreshReport” no método “Load” do formulário (dica extraída deste tópico nos fóruns da MSDN):
private void FormEtiqueta_Load(object sender, EventArgs e)
{
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("André Alves de Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW");
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Larissa Aldine Müller Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW");
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Sophie Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW");
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Fulano de Tal", "Praça da Matriz", "13480-000", "Limeira", "SP");
this.reportViewer.SetDisplayMode(Microsoft.Reporting.WinForms.DisplayMode.PrintLayout);
this.reportViewer.RefreshReport();
}
Alterando a ordem das etiquetas
Lembra que, no começo do artigo, eu disse que um inscrito tinha me questionado sobre a confecção de etiquetas no ReportViewer? Pois bem, uma dúvida que ele tinha era como alterar a ordem de impressão das etiquetas. Como você pode perceber, a ordem que o ReportViewer estabelece é que as etiquetas serão impressas primeiramente na horizontal e, quando a primeira coluna estiver totalmente preenchida, ele começará a utilizar a segunda coluna, e assim por diante.
Porém, o inscrito queria que as etiquetas fossem geradas seguindo a ordem horizontal, de forma que os registros fossem exibidos da seguinte maneira:
Após pesquisar um pouco, acabei concluindo que não é possível escolhermos nativamente no ReportViewer a ordenação desejada por esse inscrito. Mas, de qualquer forma, é possível implementarmos essa ordenação de forma manual. Para isso, temos que adicionar uma nova coluna à tabela Etiqueta, chamada “Ordenacao“, utilizando o tipo “Int32“:
Feito isso, preenchemos os valores dessa coluna com valores de forma que o mecanismo do ReportViewer seja enganado e exiba as etiquetas utilizando a nova ordenação:
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("André Alves de Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW", 1);
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Larissa Aldine Müller Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW", 3);
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Sophie Lima", "Somewhere in Bad Waldsee", "88339", "Bad Waldsee", "BW", 2);
DataSetEtiqueta.Etiqueta.AddEtiquetaRow("Fulano de Tal", "Praça da Matriz", "13480-000", "Limeira", "SP", 4);
Agora só falta configurarmos a ordenação da lista no relatório de forma que ela obedeça essa nova coluna. Para isso, vá até o relatório e, dentro da janela “Report Data“, clique com o botão direito no DataSetRelatorio e escolha a opção “Refresh“:
Com isso, o relatório reconhecerá essa nova coluna. Por fim, clique com o botão direito no canto superior esquerdo do controle “List” e escolha a opção “Tablix Properties” no menu de contexto:
Na janela “Tablix Properties“, vá até a categoria “Sorting” e adicione a coluna “Ordenacao” na lista:
Pronto! Dessa forma o relatório obedecerá a nova ordenação que estabelecemos manualmente.
A lógica para numeração da coluna “Ordenacao” dependerá da quantidade de colunas e da quantidade de etiquetas por coluna, mas, acho que deu para entender a ideia, não é mesmo?
Concluindo
A criação de relatórios para impressão de etiquetas com o ReportViewer não é complicada. Uma vez que sabemos onde as propriedades se encontram e como funciona a lógica que o ReportViewer utiliza para exibir os registros, tudo fica mais simples.
Nesse artigo você aprendeu como criar um relatório simples para a impressão de etiquetas utilizando a funcionalidade de colunas no ReportViewer. Além disso, respondi a dúvida de um inscrito sobre como criar uma ordenação customizada pra as etiquetas. Caso você queira conferir um outro artigo sobre a geração de etiquetas no ReportViewer, confira o artigo que eu utilizei como referência.
Finalmente, caso você queira ficar por dentro das novidades do meu site, além de receber dicas que eu só compartilho por e-mail, inscreva-se na minha newsletter utilizando este link ou o formulário logo abaixo.
Você sabe muito bem que tempo é dinheiro. Mas, André, o que isso tem a ver com o artigo de hoje? Tem tudo a ver! Muitos usuários de sistemas não querem ficar perdendo tempo vendo o preview da impressão se eles sabem exatamente o que vai ser impresso. Acontece com usuários das aplicações que eu desenvolvi e garanto que deve acontecer com os usuários da sua aplicação. Tanto é que um dos inscritos da minha newsletter sugeriu que eu abordasse a impressão direto na impressora com C#. E eu pensei: por que não?
Imprimindo texto direto na impressora
Definitivamente, o conteúdo mais simples de se imprimir diretamente na impressora é texto, especialmente se for tudo com uma fonte só. Nesse primeiro exemplo, quero demonstrar justamente como você pode imprimir um bloco de texto direto na impressora, sem nenhum preview.
Para isso, crie um novo projeto do tipo “Windows Forms Application“. Com esse novo projeto criado, ajuste o formulário de forma que ele fique parecido com a imagem abaixo:
Como você pode perceber, temos um ComboBox para escolher a impressora a ser utilizada (impressoraComboBox), um botão que será responsável por disparar a impressão (imprimirButton) e um TextBox com o conteúdo que deverá ser impresso (impressaoTextBox). Não esqueça de configurar o TextBox para que ele suporte múltiplas linhas (propriedade Multiline).
Antes de partirmos para o código da impressão, temos que carregar a lista de impressoras. Você não tem noção de quão fácil é listar as impressoras instaladas com o .NET Framework. A classe PrinterSettings tem uma propriedade estática chamada InstalledPrinters, que nada mais é que uma lista de strings com os nomes das impressoras. Mais simples impossível!
Dito isso, crie um método no nosso formulário (chamado CarregarListaDeImpressoras) e faça a chamada desse método no construtor do formulário:
public FormImpressaoDireta()
{
InitializeComponent();
CarregarListaDeImpressoras();
}
private void CarregarListaDeImpressoras()
{
impressoraComboBox.Items.Clear();
foreach (var printer in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
{
impressoraComboBox.Items.Add(printer);
}
}
Feito isso, vamos implementar o código para fazer a impressão do texto direto na impressora, sem o preview. A classe que temos que utilizar para implementar essa funcionalidade é a PrintDocument. Essa classe utiliza o evento PrintPage para gerenciar a impressão, ou seja, na implementação desse evento, você precisa controlar o que deve ser impresso em cada uma das páginas:
private void imprimirButton_Click(object sender, EventArgs e)
{
using (var printDocument = new System.Drawing.Printing.PrintDocument())
{
printDocument.PrintPage += printDocument_PrintPage;
printDocument.PrinterSettings.PrinterName = impressoraComboBox.SelectedItem.ToString();
printDocument.Print();
}
}
void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
var printDocument = sender as System.Drawing.Printing.PrintDocument;
if (printDocument != null)
{
using (var font = new Font("Times New Roman", 14))
using (var brush = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(
impressaoTextBox.Text,
font,
brush,
new RectangleF(0, 0, printDocument.DefaultPageSettings.PrintableArea.Width, printDocument.DefaultPageSettings.PrintableArea.Height));
}
}
}
Para testar o código, execute a aplicação, escolha uma impressora no ComboBox, digite um texto na caixa de texto e clique em “Imprimir“:
A propósito, você sabia que o Word consegue gerar textos de exemplo (esse que começa em “Lorem ipsum“)? Aprenda como neste link.
E se não couber em uma página?
Se o conteúdo da caixa de texto não couber em uma página, somente a primeira página será impressa. E como conseguimos ajustar o código para que ele se comporte corretamente independentemente do número de páginas? Para isso, temos que alterar um pouco o código de forma que somente o texto que cabe em cada página seja impresso. Como fazer isso? Veja o resultado:
private string _texto;
private void imprimirButton_Click(object sender, EventArgs e)
{
using (var printDocument = new System.Drawing.Printing.PrintDocument())
{
printDocument.PrintPage += printDocument_PrintPage;
printDocument.PrinterSettings.PrinterName = impressoraComboBox.SelectedItem.ToString();
_texto = impressaoTextBox.Text;
printDocument.Print();
}
}
void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
var printDocument = sender as System.Drawing.Printing.PrintDocument;
if (printDocument != null)
{
using (var fonte = new Font("Times New Roman", 14))
using (var brush = new SolidBrush(Color.Black))
{
int caracteresNaPagina = 0;
int linhasPorPagina = 0;
e.Graphics.MeasureString(
_texto, fonte, e.MarginBounds.Size, StringFormat.GenericTypographic,
out caracteresNaPagina, out linhasPorPagina);
e.Graphics.DrawString(
_texto.Substring(0, caracteresNaPagina),
fonte,
brush,
e.MarginBounds);
_texto = _texto.Substring(caracteresNaPagina);
e.HasMorePages = _texto.Length > 0;
}
}
}
Como você pode ver, agora temos uma “variável” no nível do formulário chamada “_texto“. Essa variável guardará sempre o texto que está faltando imprimir. Portanto, quando clicamos no botão “Imprimir“, nós guardamos o texto completo da caixa de texto nessa variável. Depois, conforme formos imprimindo o texto de cada página, iremos ajustando o valor dessa variável para que ela guarde somente o texto que está faltando.
E como é que medimos a quantidade de texto que cabe em uma página? Fácil! É só utilizarmos o método MeasureString da classe Graphics. Esse método recebe o texto, a fonte e as dimensões da área disponível para impressão, retornando a quantidade de caracteres e linhas que conseguimos imprimir nessa área:
int caracteresNaPagina = 0;
int linhasPorPagina = 0;
e.Graphics.MeasureString(
_texto, fonte, e.MarginBounds.Size, StringFormat.GenericTypographic,
out caracteresNaPagina, out linhasPorPagina);
Por fim, a outra alteração importante na implementação do evento PrintPage foi feita na última linha, onde indicamos se ainda temos outra página para imprimir ou não (propriedade HasMorePages). Devemos configurar essa propriedade como “true” caso ainda tenhamos texto para ser impresso (Length maior que zero).
Imprimindo imagens e formas geométricas
Da mesma forma que conseguimos imprimir texto utilizando a classe PrintDocument, nós conseguimos imprimir imagens e formas geométricas também. Para isso, ao invés de utilizarmos o método DrawString, temos que utilizar os métodos DrawImage ou Draw*** (onde “***” é o nome da figura geométrica que você quer desenhar).
Por exemplo, para imprimirmos uma imagem do arquivo “logo.png” (localizada no mesmo diretório da aplicação), poderíamos utilizar o seguinte código:
void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
var printDocument = sender as System.Drawing.Printing.PrintDocument;
if (printDocument != null)
{
e.Graphics.DrawImage(Image.FromFile("logo.png"), new Point(5, 5));
}
}
Já para imprimirmos um polígono qualquer, temos que substituir a chamada do método DrawImage por DrawPolygon:
e.Graphics.DrawPolygon(
new Pen(Color.Red),
new[]
{
new Point(30, 30),
new Point(80, 150),
new Point(20, 70),
new Point(100, 100),
new Point(30, 30)
});
Imprimindo um RichTextBox formatado
O exemplo de impressão de texto que eu demonstrei nesse artigo não considera a impressão de textos ricos (com formatação). Para conseguirmos imprimir esse tipo de texto com formatação, é necessária muita gambiarra. Durante a minha pesquisa para escrever esse artigo, encontrei dois exemplos de impressão de RichTextBox:
Algumas pessoas já me perguntaram como fazer para imprimir diretamente na porta LPT1, a fim de imprimir texto puro em impressoras matriciais. Sempre que alguém me pergunta isso, eu encaminho o link para o componente que o Carlos dos Santos desenvolveu, que implementa justamente essa funcionalidade:
Normalmente, quando queremos imprimir informações a partir dos nossos sistemas, utilizamos alguma ferramenta de geração de relatórios (como Report Viewer ou Crystal Reports). Porém, em algumas situações precisamos ter o controle total do conteúdo que será impresso, principalmente as posições onde as informações serão impressas e se queremos ou não exibir um preview antes de imprimir. Nesse artigo você aprendeu a imprimir textos, imagens e figuras geométricas diretamente na impressora. Dessa forma, nas situações em que você precisar desse tipo de controle total da sua impressão, você saberá como proceder.
E você, já teve que fazer algo parecido? Como você acabou resolvendo essa situação? Da mesma forma que eu resolvi ou de alguma outra maneira? Conte nos comentários como foram as suas experiências com a impressão de informações em aplicativos desenvolvidos com o .NET Framework.
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se aqui ou utilizando o formulário logo abaixo.
Uma das coisas mais legais de manter uma newsletter é receber os e-mails com as dúvidas dos inscritos. É impressionante como cada pessoa passa pelos problemas mais inusitados a cada dia. Se você já mandou um e-mail com alguma dúvida para mim, você sabe que eu sempre respondo (nem sempre eu consigo solucionar a questão, mas, garanto que ao menos eu vou responder o seu e-mail tentando solucioná-la).
Enfim, esses dias atrás eu recebi uma dúvida muito interessante de um dos inscritos. Ele tem um relatório no Report Viewer com uma expressão onde ele quer formatar parte dessa expressão em negrito (adaptei o texto da questão para bater com o exemplo que vou dar aqui neste artigo):
Seguinte. Tenho a expressão: =”Id: ” & Fields!ID.Value & ” / Descrição: ” & Fields!Descricao.Value Queria deixar a string “Id:” e “Descrição:” em negrito, saberia me dizer como posso fazer isso?
Quer aprender como resolver esse problema junto com a gente? Vamos lá!
Opção 1: não utilize expressões!
A primeira opção é a mais simples de todas. O designer do Report Viewer suporta nativamente a formatação mista de TextBoxes. Ou seja, se você tem um texto em um TextBox e quer formatar somente uma parte dele em negrito (ou até mesmo com cores diferentes), basta selecionar a parte do texto que você quer formatar e aplicar a formatação desejada.
Por exemplo, imagine que tenhamos um TextBox com o texto “Formatações diferentes no mesmo TextBox!!!“. Como fazemos para deixar a palavra “diferentes” em vermelho e a palavra “mesmo” em negrito? Simples! Basta selecionar a palavra “diferentes” e aplicar a cor vermelha; depois selecione a palavra “mesmo” e aplicar o negrito!
Legal, André. Mas, na pergunta do inscrito ele estava usando valores de campos no TextBox. Como é que consigo colocar valores de campos junto com outros textos no mesmo TextBox sem usar expressões? Fácil, fácil! É só arrastar os campos do DataSet para dentro do TextBox, veja só:
Olha só o resultado em tempo de execução:
Opção 2: expressões + placeholder HTML
Se você realmente tiver uma paixão por expressões no Report Viewer e não quiser abrir mão de que o conteúdo desse TextBox seja uma expressão, não tem problema. Você sabia que conseguimos transformar qualquer TextBox do Report Viewer em um placeholder (espaço reservado) para HTML? Ou seja, com um simples ajuste em qualquer TextBox do Report Viewer, podemos colocar código HTML que será interpretado pelo mecanismo de renderização!
O grande problema é que a Microsoft conseguiu esconder muito bem essa funcionalidade. Mas, não se preocupe. Eu vou mostrar para você como fazer para criar um placeholder de forma que consigamos colocar código HTML dentro de qualquer TextBox do Report Viewer!
Para criar um placeholder no Report Viewer, clique uma vez no TextBox (de forma que ele fique selecionado) e então, clique novamente no TextBox (de forma que o modo de edição seja ativado e o cursor fique piscando, esperando que você digite algo). Agora clique com o botão direito no TextBox e escolha a opção “Create Placeholder“:
Na janela de propriedades do placeholder, escolha a opção “HTML – Interpret HTML tags as styles” e clique no botão de expressão para o valor:
Ao ativarmos a opção de interpretação de HTML, nós podemos utilizar livremente as tags de estilo HTML dentro da nossa expressão (por exemplo, a tag “<b>” para texto em negrito). Dessa forma, para que o nosso texto fique exatamente como demonstrado na primeira opção, poderíamos utilizar a seguinte expressão:
Lembre-se que, como estamos trabalhando com um placeholder HTML, você poderia muito bem utilizar outras tags de formatação (como “<i>” para itálico). Nem todas as tags são suportadas, aliás, se você conferir a documentação do Report Viewer, verá que, na realidade, poucas tags são suportadas.
Concluindo
É possível, sim, utilizar formatações diferentes dentro de um TextBox no Report Viewer. Inclusive, como você conferiu nesse artigo, você pode fazer isso de duas formas. O designer do Report Viewer suporta nativamente formatações mistas no mesmo TextBox, portanto, sugiro que você tente fazer a formatação diretamente no designer do relatório. Porém, caso você precise de um controle maior (ou se a formatação que você estiver querendo fazer não seja suportada pelo designer), você pode criar um placeholder HTML e utilizar as tags compatíveis para fazer formatações mistas no mesmo TextBox.
Você gostou desse conteúdo? Ficou com alguma dúvida? Tem sugestões de melhorias ou até mesmo outros temas? Por favor, adicione um comentário e fale o que você achou!
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 aqui ou utilizando o formulário logo abaixo.
No quesito geração de relatórios para aplicativos desenvolvidos com a plataforma Microsoft, sempre surge essa dúvida sobre qual ferramenta é melhor: Crystal Reports ou Report Viewer?
Nesse replay da transmissão que eu fiz duas semanas atrás no Periscope, eu faço um comparativo entre o Crystal Reports e Report Viewer, mostrando os prós e contras, pontos fortes e fraquezas dessas duas ferramentas.
Maturidade
Você sabia que a primeira versão do Crystal Reports foi lançada em 1991? Nessa época eu (e muita gente que me acompanha) ainda era uma criança! Muitas empresas já estiveram por trás do Crystal Reports: Crystal Services, Crystal Decisions, Business Objects e, a atual mantenedora do Crystal Reports, a SAP. Durante todos esses anos, o Crystal Reports tem evoluído, mas, na minha opinião, somente a “casca” do designer foi reformulada (adicionaram a interface com ribbon, por exemplo). Quando você clica para alterar alguma fórmula ou configurações da fonte de dados, aquela tela arcaica da versão 8 de 10 anos atrás é exibida.
Já a primeira versão do Report Viewer foi lançada em 2004, como um addon para o SQL Server 2000. É muito mais novo e tem sempre evoluído consideravelmente em cada versão do Visual Studio / SQL Server. O designer é muito simples e possui um controle tabular (inexistente no Crystal Reports, o que faz com que tenhamos que usar TextBoxes para construir tabelas – um pesadelo), porém, falta algumas funcionalidades. Por exemplo, não dá para justificar texto no Report Viewer!
Portanto, na categoria maturidade, ponto para o Crystal Reports.
Se você já teve que distribuir uma aplicação que usa Crystal Reports, você sabe a dificuldade que é. Para que tudo funcione corretamente, você tem que instalar uma runtime gigantesca e rezar para que tudo funcione perfeitamente. Já o Report Viewer, a runtime é minúscula e, caso você queira, é possível distribuir somente três dlls junto com a sua aplicação e tudo irá funcionar perfeitamente.
Novamente, ponto para o Report Viewer.
Suporte
Como o Crystal Reports já passou por muitos donos, imagine a bagunça que ficou o suporte. Os fóruns da SAP são muito complicados e difíceis de entender. Nem sempre as pessoas respondem às suas questões, além do pessoal do time muitas vezes enrolar para resolver algum problema.
Já para o Report Viewer, você tem os fóruns da MSDN. São bastante movimentados e normalmente você consegue tirar as suas dúvidas, principalmente nos fóruns em inglês. E se você (ou sua empresa) tiver uma assinatura MSDN, você pode utilizar os tickets prioritários para entrar em contato diretamente com a Microsoft em casos de problemas graves.
Mais uma vez, ponto para o Report Viewer.
Documentação e conteúdo
O Crystal Reports é muito mais antigo, dessa forma, existe MUITO mais material sobre ele na Internet do que conteúdo sobre Report Viewer. Principalmente se estivermos falando de conteúdo em português.
Nessa categoria, ponto para o Crystal Reports.
Relatórios complexos
Como mencionei anteriormente, parece que a Microsoft desenvolveu o Report Viewer até os 95%. Falta os 5% para gerarmos relatórios extremamente complexos. Nesse ponto, o Crystal Reports atende a qualquer tipo de relatório complexo que você tiver que desenvolver.
Portanto, ponto para o Crystal Reports
Resultado: 3 x 3
Poderíamos continuar esse comparativo até o infinito e pode até ser que a balança penderia para um dos dois lados. Mas, propositalmente parei no empate, porque a pergunta certa não é “qual a melhor ferramenta“, mas sim, “qual a situação em que eu devo usar cada uma das ferramentas“.
Se você tiver uma aplicação simples, com relatórios não muito elaborados, por favor, utilize o Report Viewer e seja feliz. O designer é muito mais simples do que o Crystal Reports e você conseguirá ser muito mais produtivo. Porém, se a sua aplicação demandar relatórios super-complexos, eu recomendo o Crystal Reports. Você pode até pensar em um ambiente híbrido onde 90% dos relatórios são desenvolvidos com o Report Viewer e os outros 10% dos relatórios complexos são desenvolvidos com o Crystal Reports.
Me siga no Periscope!
Eu ficaria muito, mas muito, feliz se você me adicionasse no Periscope e tentasse acompanhar as minhas transmissões ao vivo. Seria muito legal se pudéssemos trocar uma ideia sobre os assuntos que eu apresento nessa plataforma que eu estou experimentando. Amanhã acontece a minha próxima transmissão, onde eu vou mostrar para você um overview das certificações Microsoft para desenvolvedores. Me siga lá e acompanhe ao vivo: @andrealveslima.
Esses dias atrás eu publiquei um e-book sobre o desenvolvimento de relatórios com o Report Viewer (a propósito, caso você se interesse, ele está disponível aqui). Durante todo o processo de escrita desse e-book, eu utilizei o Visual Studio 2013 Community, que já vem com o Report Viewer instalado por padrão.
Entretanto, poucos dias depois do lançamento, um dos compradores me enviou um e-mail falando que ele não estava encontrando o Report Viewer no Visual Studio 2015 Community. Achei muito estranho, então, resolvi investigar. E não é que o Report Viewer realmente não vem mais instalado por padrão junto com o Visual Studio 2015?
Mas, não se preocupe, a Microsoft não desistiu do Report Viewer. Neste artigo, eu vou te explicar como adicionar o Report Viewer no Visual Studio 2015.
Marque Microsoft SQL Server Data Tools na hora da instalação!
A partir do Visual Studio 2015, a Microsoft resolveu simplificar um pouco as opções de instalação. Por um lado, isso é bom porque simplifica a vida da pessoa que está instalando, porém, por outro lado, isso acaba criando problemas como este que estou descrevendo neste artigo.
No Visual Studio 2015 Community, por exemplo, podemos escolher pela instalação “típica“, que instala as linguagens C# e VB.NET para a criação de aplicativos web e desktop. Porém, essa opção não instala o Report Viewer!
Dessa forma, na hora de instalar o Visual Studio 2015, caso você precise utilizar o Report Viewer, escolha a opção de instalação “Custom” e, na próxima tela, não esqueça de marcar a opção “Microsoft SQL Server Data Tools“:
Ao fazer isso, o Report Viewer será instalado em conjunto com o Visual Studio 2015.
E se eu já tiver instalado o Visual Studio?
Mas, André, se eu cheguei até este post, provavelmente eu já instalei o Visual Studio 2015 sem ter escolhido o Report Viewer. E agora, como é que eu devo proceder?
Calma, não se preocupe, é possível ativar o Report Viewer mesmo depois de já ter instalado o Visual Studio 2015. Para isso, vá até o Painel de Controle e abra a opção para listar os programas instalados. Na tela “Programs and Features“, encontre o Visual Studio 2015 e clique em “Change“:
Na tela que se abre, clique em “Modify“, marque a opção “Microsoft SQL Server Data Tools” na lista de componentes do Visual Studio e clique em “Update“:
Depois de algum tempo, o instalador será concluído:
E, a partir de agora, você terá disponível o Report Viewer tanto na Toolbox (caixa de ferramentas) quanto na tela em que você adiciona novos itens no projeto:
Concluindo
Essa alteração que a Microsoft fez na instalação do Visual Studio 2015 removendo o Report Viewer da instalação padrão vai pegar muita gente desprevenida. Eu mesmo fiquei assustado quando o leitor do meu e-book fez essa observação.
Mas, felizmente, a Microsoft não foi maluca e removeu o Report Viewer do Visual Studio 2015 (como muita gente está falando por aí). Basta você seguir essas instruções e você conseguirá continuar desenhando normalmente os seus relatórios com o Report Viewer no Visual Studio 2015.
Se você gostou desse artigo, ajude espalhar o conhecimento compartilhando no Twitter:
Não consegue encontrar o Report Viewer no Visual Studio 2015? Confira aqui como ativá-lo:… Click To Tweet
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 logo abaixo.
Um erro comum que muitas pessoas cometem ao trabalhar com o Report Viewer no Windows Forms é criar um formulário (do Windows Forms) para cada relatório do sistema. Eu diria que o próprio designer do Report Viewer no Visual Studio induz os desenvolvedores a esse erro, uma vez que ele possibilita a escolha do relatório na smart tag do controle. No artigo de hoje, eu vou te mostrar como exibir múltiplos relatórios no mesmo formulário com o Report Viewer.
Criar um formulário para cada relatório não é a melhor maneira de se trabalhar. Imagine que você tenha 15 relatórios no seu sistema. Trabalhando dessa forma, você teria 15 formulários só para fazer a exibição de relatórios. Já pensou se você quiser alterar alguma propriedade do controle do Report Viewer no sistema todo? Por exemplo, digamos que você queira desabilitar o botão de exportação do Report Viewer. Você teria que alterar cada um dos 15 formulários. Nada prático, não é mesmo?
Criando dois relatórios de exemplo
Para exemplificar essa problemática, vamos criar um novo projeto do tipo “Windows Forms Application“. Dentro desse novo projeto, adicione dois DataSets tipados: um chamado “DataSetCliente” e outro chamado “DataSetFornecedor“. No DataSetCliente, adicione uma DataTable chamada “Cliente” e, no DataSetFornecedor, adicione uma DataTable chamada “Fornecedor“:
Feito isso, adicione dois novos relatórios ao projeto: um relatório chamado “ReportCliente” e outro chamado “ReportFornecedor“. Ajuste o layout dos relatórios para que eles fiquem parecidos com as imagens abaixo:
Como você pode perceber, o layout desses relatórios é extremamente simples: um título no cabeçalho do relatório e uma “Table” na área de detalhes listando as informações dos DataSets que criamos anteriormente.
O jeito “errado” – um formulário para cada relatório
Agora que já temos os DataSets e relatórios preparados, precisamos exibi-los de alguma forma. A maneira mais fácil de fazermos isso é criarmos um formulário para cada um desses relatórios. Para fazermos isso, temos que adicionar dois novos formulários ao nosso projeto, arrastarmos um controle do Report Viewer em cada um deles e, finalmente, temos que escolher o relatório desejado utilizando a smart tag do controle:
Em seguida, temos que carregar os dados do DataSet dentro do construtor de cada formulário:
Finalmente, crie um novo formulário (ou altere o “Form1” que é criado automaticamente pelo Visual Studio) que servirá como menu para chamar um relatório ou outro:
private void clientesButton_Click(object sender, EventArgs e)
{
var reportCliente = new FormReportCliente();
reportCliente.Show();
}
private void fornecedoresButton_Click(object sender, EventArgs e)
{
var reportFornecedor = new FormReportFornecedor();
reportFornecedor.Show();
}
E com isso conseguimos exibir os dois relatórios sem problema algum. O real problema surge quando quisermos customizar alguma coisa no formulário de exibição dos relatórios no nosso aplicativo. Para isso teríamos que fazer a mesma alteração em cada um dos formulários de exibição de relatórios. Quando só temos dois relatórios até que não é difícil, mas, quanto mais relatórios tivermos no nosso projeto, mais difícil fica a manutenção ao utilizarmos essa sistemática.
A alternativa – um único formulário para todos os relatórios
Após termos conferido o jeito “errado” de exibirmos múltiplos relatórios com o Report Viewer, que tal conferirmos uma alternativa com um custo de manutenção muito mais baixo? A alternativa é criarmos um único formulário que será utilizado para fazermos a exibição de todos os relatórios do nosso sistema. Esse formulário receberá por parâmetro todas as informações necessárias para carregar o relatório e exibi-lo.
Para isso, adicione um novo formulário no projeto, chamado “FormRelatorio“. Esse formulário deverá ter um controle do Report Viewer, porém, não escolheremos nenhum relatório no designer (faremos isso no code-behind utilizando os parâmetros recebidos).
Feito isso, vá para o code-behind do formulário e altere a visibilidade do construtor para “private“. Isso porque criaremos um novo método público e estático no formulário que será encarregado de criar e exibir uma nova instância de “FormRelatorio” (isso facilita a chamada para a exibição de um novo relatório – não se preocupe, você entenderá melhor o motivo daqui a pouco). Além disso, vamos adicionar alguns parâmetros no construtor: “path“, “isEmbeddedResource“, “dataSources” e “reportParameters“:
O parâmetro “path” conterá o caminho para o relatório, que pode ser um arquivo em disco ou um “embedded resource” da aplicação (quando o relatório é adicionado dentro do próprio projeto, como estamos fazendo no exemplo deste artigo). É justamente por esse motivo que precisamos do segundo parâmetro (“isEmbeddedResource“), no qual indicaremos se o “path” corresponde a um caminho em disco ou um caminho para um “embedded resource” do projeto.
Em seguida, temos os parâmetros “dataSources” e “reportParameters“, que são dois dicionários com chave string e valor object. Eles são utilizados para popular as fontes de dados do relatório e os parâmetros necessários para o relatório. Note que “reportParameters” é opcional, uma vez que a maioria dos relatórios não utilizam parâmetros.
E como é que implementamos a lógica para cada um desses parâmetros? Confira no código abaixo:
private FormRelatorio(string path, bool isEmbeddedResource, Dictionary<string, object> dataSources, Dictionary<string, object> reportParameters = null)
{
InitializeComponent();
// path + isEmbeddedResource.
if (isEmbeddedResource)
this.reportViewer.LocalReport.ReportEmbeddedResource = path;
else
this.reportViewer.LocalReport.ReportPath = path;
// dataSources.
foreach (var dataSource in dataSources)
{
var reportDataSource = new Microsoft.Reporting.WinForms.ReportDataSource(dataSource.Key, dataSource.Value);
this.reportViewer.LocalReport.DataSources.Add(reportDataSource);
}
// reportParameters.
if (reportParameters != null)
{
var reportParameterCollection = new List<Microsoft.Reporting.WinForms.ReportParameter>();
foreach (var parameter in reportParameters)
{
var reportParameter = new Microsoft.Reporting.WinForms.ReportParameter(parameter.Key, parameter.Value.ToString());
reportParameterCollection.Add(reportParameter);
}
this.reportViewer.LocalReport.SetParameters(reportParameterCollection);
}
}
Simples, não? Nós basicamente utilizamos os parâmetros do construtor para configurar o nosso controle do Report Viewer. É justamente isso que é feito no “background” pelo Visual Studio quando você seleciona um relatório específico no designer do Report Viewer.
Agora que já temos o construtor com os parâmetros e a sua implementação, vamos criar aquele método estático que será responsável pela criação e exibição do “FormRelatorio“. Para isso, dentro do “FormRelatorio“, adicione o seguinte método:
public static void ShowReport(string path, bool isEmbeddedResource, Dictionary<string, object> dataSources, Dictionary<string, object> reportParameters = null)
{
var formRelatorio = new FormRelatorio(path, isEmbeddedResource, dataSources, reportParameters);
formRelatorio.Show();
}
Finalmente, podemos voltar ao nosso formulário de menu para alterá-lo de forma que ele utilize o nosso “FormRelatorio“. Veja só como fica o código alterado:
Muito mais organizado, não é mesmo? Com isso, ao invés de ficar criando um formulário para cada novo relatório, podemos simplesmente utilizar e evoluir o nosso “FormRelatorio“.
Concluindo
Neste artigo você aprendeu que não é uma boa prática criar um formulário para cada relatório da nossa aplicação. Ao invés disso, é melhor criarmos um formulário único que será utilizado para exibir todo e qualquer relatório disponível no nosso sistema.
Agora, conte para mim nos comentários: você já utiliza hoje em dia um formulário padrão para exibir todos os relatórios? Ou você não sabia desse detalhe e estava criando um formulário para cada relatório do seu sistema? Se a sua resposta for a segunda opção, ajuste o seu projeto agora mesmo para que ele tenha somente um formulário de exibição de relatórios, antes que a situação saia do controle!
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 logo abaixo.
Não é segredo para ninguém que os arquivos PDF dominaram a questão de exportação de documentos em sistemas de informação. Você gera uma fatura no seu sistema? Talvez você tenha que gerar um boleto ou qualquer outro tipo de relatório? Então provavelmente a exportação desses documentos para PDF é um requisito básico da sua aplicação.
Quando trabalhamos com ferramentas de geração de relatórios como Crystal Reports ou Report Viewer, normalmente não precisamos nos preocupar com essa funcionalidade, uma vez que a exportação para PDF está implementada nativamente nessas ferramentas. Porém, e se precisarmos gerar um PDF “na mão“? Quais são as nossas opções? Despois de ter sido questionado sobre esse tema várias vezes, resolvi publicar este artigo explicando como trabalhar com arquivos PDF no C#.
Se você caiu neste artigo procurando maneiras de gerar arquivos PDF no C#, provavelmente você já sacou que essa funcionalidade não está implementada nativamente no .NET Framework. Dessa forma, precisamos trabalhar com bibliotecas externas para conseguir realizar essa tarefa.
Depois de pesquisar um bom bocado, cheguei à conclusão que temos duas principais opções gratuitas de bibliotecas: a PDFSharp e a iTextSharp. Vamos analisar cada uma dessas opções e ver qual compensa mais?
O objetivo
Para fazer esse comparativo, temos que estabelecer um objetivo. Via de regra, quando pensamos em geração de PDFs, quais são os elementos que precisaremos incluir nos nossos documentos? Essa foi a minha conclusão:
– Textos – Figuras geométricas – Imagens
Portanto, para servir de base, criei no Microsoft Word um documento que seria a nossa “base” para testar cada uma das bibliotecas (caso queira, você pode baixa-lo aqui):
Como você pode ver, primeiramente temos trechos de texto alinhados à esquerda, centralizado, à direita e justificado. Logo em seguida, temos duas figuras geométricas e, finalmente, uma imagem. A imagem que eu utilizei é a “penguins.jpg“, que vem como figura de exemplo no Windows 7. Se você não estiver utilizando o Windows 7, você pode baixa-la aqui.
Agora que já temos o nosso objetivo traçado, vamos começar a análise das bibliotecas, começando com a PDFSharp.
Opção 1 – Biblioteca PDFSharp
Para testarmos as duas bibliotecas, vamos criar projetos do tipo “Console Application“. Dessa forma, garantimos que as bibliotecas não estão referenciando nenhuma dll relacionada a UI, permitindo que as utilizemos essas bibliotecas até mesmo em dlls nas camadas de negócios ou serviços.
Para a nossa sorte, tanto a biblioteca PDFSharp quanto a biblioteca iTextSharp estão disponíveis como pacotes do NuGet. Dessa forma, adicioná-las ao nosso projeto é muito simples: basta procurar pelo nome da biblioteca na tela de gerenciamento de pacotes do NuGet e escolher a opção para instalá-la (ou, caso você prefira, você pode também utilizar o Package Manager Console – se você estiver com dúvidas neste processo, confira o artigo que eu escrevi sobre como gerenciar pacotes do NuGet no Visual Studio):
Caso você escolha ir pelo caminho tradicional, você pode também fazer o download da biblioteca e referenciá-la no seu projeto. Uma vez referenciada a biblioteca, podemos começar a construir o nosso exemplo.
Como definido no nosso documento “base“, a primeira informação que temos que adicionar no nosso PDF são os textos com os diferentes alinhamentos. Porém, antes disso, precisamos instanciar algumas variáveis necessárias para a criação de qualquer PDF com o PDFSharp: um PdfDocument, uma PdfPage, um XGraphics, um XTextFormatter e um XFont (esses dois últimos somente se tivermos que trabalhar com texto, que é justamente o nosso caso):
var document = new PdfSharp.Pdf.PdfDocument();
var page = document.AddPage();
var graphics = PdfSharp.Drawing.XGraphics.FromPdfPage(page);
var textFormatter = new PdfSharp.Drawing.Layout.XTextFormatter(graphics);
var font = new PdfSharp.Drawing.XFont("Calibri", 14);
Como você pode perceber, até agora tudo simples. Primeiro criamos um PdfDocument. Depois, criamos uma PdfPage chamando o método AddPage do PdfDocument. Em seguida, criamos um XGraphics que servirá para desenharmos (e escrevermos) naquela página do PdfDocument. Finalmente, criamos um XTextFormatter apontando para o XGraphics e uma instância de XFont com a fonte “Calibri” tamanho 14, que será a fonte utilizada em todos os nossos blocos de texto.
Feito isso, podemos começar a escrever os textos na nossa página do arquivo PDF. E é logo de cara que veremos uma das desvantagens da biblioteca PDFSharp:
// Textos.
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Left;
textFormatter.DrawString("Aqui temos um parágrafo alinhado à esquerda.", font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 30, page.Width - 60, page.Height - 60));
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Center;
textFormatter.DrawString("Aqui temos um parágrafo centralizado.", font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 60, page.Width - 60, page.Height - 60));
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Right;
textFormatter.DrawString("Aqui temos um parágrafo alinhado à direita.", font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 90, page.Width - 60, page.Height - 60));
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Justify;
textFormatter.DrawString("E, finalmente, aqui temos um parágrafo justificado. Aqui precisamos de mais texto para ver se o texto está realmente sendo justificado! Confira as outras categorias do meu site para artigos relacionados a outras tecnologias de desenvolvimento de software!",
font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 120, page.Width - 60, page.Height - 60));
Conseguiu entender o grande problema no trecho de código acima? Para TUDO o que você for escrever ou desenhar com o PDFSharp, você precisa especificar um “XRect“, que é basicamente a posição em que aquele elemento será desenhado na página do arquivo PDF. Ou seja, não existe um conceito real de “documento” ou “parágrafos” com o PDFSharp. Isso é extremamente irritante quando trabalhamos com textos, pois, dependendo do tamanho e tipo da fonte, o texto ocupará um espaço diferente na página.
Imagine que você tenha um texto longo que não caiba em uma página. É você que terá que controlar o que será impresso na primeira página e o que será impresso na segunda. Sacou a complexidade?
Enfim, continuando o nosso exemplo, agora temos que desenhar um retângulo e um círculo logo abaixo dos nossos blocos de texto. Essa é uma tarefa extremamente fácil com a biblioteca PDFSharp. Basta utilizarmos os métodos DrawRectangle e DrawEllipse da nossa variável XGraphics:
// Figuras geométricas.
graphics.DrawRectangle(PdfSharp.Drawing.XPens.Black, PdfSharp.Drawing.XBrushes.White, new PdfSharp.Drawing.XRect(100, 210, 180, 110));
graphics.DrawEllipse(PdfSharp.Drawing.XPens.Black, PdfSharp.Drawing.XBrushes.White, new PdfSharp.Drawing.XRect(350, 210, 100, 100));
Logo em seguida, utilizamos novamente a variável XGraphics para desenharmos a imagem, porém, dessa vez utilizamos o método DrawImage (não esqueça de colocar o arquivo penguins.jpg na basta bin/debug do seu projeto!):
Vamos dar uma olhada novamente no código completo:
var document = new PdfSharp.Pdf.PdfDocument();
var page = document.AddPage();
var graphics = PdfSharp.Drawing.XGraphics.FromPdfPage(page);
var textFormatter = new PdfSharp.Drawing.Layout.XTextFormatter(graphics);
var font = new PdfSharp.Drawing.XFont("Calibri", 14);
// Textos.
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Left;
textFormatter.DrawString("Aqui temos um parágrafo alinhado à esquerda.", font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 30, page.Width - 60, page.Height - 60));
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Center;
textFormatter.DrawString("Aqui temos um parágrafo centralizado.", font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 60, page.Width - 60, page.Height - 60));
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Right;
textFormatter.DrawString("Aqui temos um parágrafo alinhado à direita.", font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 90, page.Width - 60, page.Height - 60));
textFormatter.Alignment = PdfSharp.Drawing.Layout.XParagraphAlignment.Justify;
textFormatter.DrawString("E, finalmente, aqui temos um parágrafo justificado. Aqui precisamos de mais texto para ver se o texto está realmente sendo justificado! Confira as outras categorias do meu site para artigos relacionados a outras tecnologias de desenvolvimento de software!",
font, PdfSharp.Drawing.XBrushes.Black, new PdfSharp.Drawing.XRect(30, 120, page.Width - 60, page.Height - 60));
// Figuras geométricas.
graphics.DrawRectangle(PdfSharp.Drawing.XPens.Black, PdfSharp.Drawing.XBrushes.White, new PdfSharp.Drawing.XRect(100, 210, 180, 110));
graphics.DrawEllipse(PdfSharp.Drawing.XPens.Black, PdfSharp.Drawing.XBrushes.White, new PdfSharp.Drawing.XRect(350, 210, 100, 100));
// Imagem.
graphics.DrawImage(PdfSharp.Drawing.XImage.FromFile("penguins.jpg"), 80, 350, 400, 300);
document.Save("output.pdf");
System.Diagnostics.Process.Start("output.pdf");
E com isso conseguimos gerar um documento bem parecido com o nosso documento “base” (claro que algumas posições dos elementos não estão 100% – esse também não era o objetivo deste artigo):
Para outras dezenas de exemplos utilizando a biblioteca PDFSharp, confira este link do Wiki oficial:
A nossa segunda opção é a biblioteca iTextSharp, que nada mais é que um “port” para .NET da biblioteca iText (desenvolvida originalmente em java). Como mencionei anteriormente, essa biblioteca também está disponível como um pacote do NuGet, portanto, para adicioná-la, basta procurar por iTextSharp na tela de gerenciamento de pacotes do NuGet:
Também é possível baixar a biblioteca diretamente do seu repositório no GitHub, caso você preferir. Aí é só adicionar as referências manualmente no seu projeto.
Como você provavelmente vai perceber, apesar de ter algumas vantagens (como o conceito de parágrafos), as nomenclaturas e convenções da biblioteca iTextSharp são bem mais confusas do que o PDFSharp. Isso provavelmente porque o iTextSharp foi portado do java, e não desenvolvido diretamente no C# como é o caso do PDFSharp.
Para criarmos um documento PDF com o iTextSharp, precisamos de uma Stream. Como queremos salvar o arquivo PDF em disco, criaremos um FileStream. É dentro deste bloco “using” que colocaremos todo o código de geração do arquivo PDF:
using (var fileStream = new System.IO.FileStream("output.pdf", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
{
}
A primeira ação que temos que fazer dentro desse bloco é instanciar um Document e um PdfWriter, além de abrir o documento utilizando o método “Open” de forma que possamos editá-lo:
var document = new iTextSharp.text.Document();
var pdfWriter = iTextSharp.text.pdf.PdfWriter.GetInstance(document, fileStream);
document.Open();
E agora começam as complicações. Lembra que utilizamos a fonte “Calibri” no exemplo com o PDFSharp? Pois bem, no PDFSharp só tivemos que instanciar um XFont passando o nome “Calibri” e pronto, a biblioteca encontrou a fonte no diretório de fontes do Windows e tudo certo. Já com a biblioteca iTextSharp, ela só suporta por padrão as fontes Courier, Helvetica, Times New Roman, Symbol e ZapfDingBats (eu nunca tinha ouvido falar dessa fonte). Para utilizarmos qualquer outra fonte, temos que começar a fazer umas “gambis“, que eu encontrei nestes links:
Basicamente, temos que registrar o diretório de fontes do Windows no iTextSharp aí ele passa a reconhecer todas as fontes instaladas no seu computador:
iTextSharp.text.FontFactory.RegisterDirectory("C:\\WINDOWS\\Fonts");
var font = iTextSharp.text.FontFactory.GetFont("Calibri", 14);
Enfim, uma vez resolvido esse problema da fonte, vamos partir para a criação dos textos? Felizmente, ao contrário do PDFSharp, a biblioteca iTextSharp tem o conceito de parágrafos. Dessa forma, não precisamos ficar posicionando os textos dentro do nosso PDF utilizando coordenadas. E a biblioteca também lida automaticamente com margens e quebras de página. Bem melhor que o PDFSharp nesse quesito:
// Textos.
var paragraph = new iTextSharp.text.Paragraph("Aqui temos um parágrafo alinhado à esquerda.", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_LEFT;
document.Add(paragraph);
paragraph = new iTextSharp.text.Paragraph("Aqui temos um parágrafo centralizado.", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_CENTER;
document.Add(paragraph);
paragraph = new iTextSharp.text.Paragraph("Aqui temos um parágrafo alinhado à direita.", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_RIGHT;
document.Add(paragraph);
paragraph = new iTextSharp.text.Paragraph("E, finalmente, aqui temos um parágrafo justificado. Aqui precisamos de mais texto para ver se o texto está realmente sendo justificado! Confira as outras categorias do meu site para artigos relacionados a outras tecnologias de desenvolvimento de software!", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_JUSTIFIED;
document.Add(paragraph);
Penei um pouco para encontrar o que eu deveria setar na propriedade “Alignment“, uma vez que ela é uma propriedade int e não um enum. Depois de pesquisar um pouco, consegui encontrar a resposta neste link:
Agora que já temos os textos propriamente inseridos no nosso documento e com o alinhamento conforme definimos no modelo, vamos desenhar o retângulo e o círculo logo abaixo do último parágrafo. Para desenharmos figuras geométricas e imagens no PDF, temos que utilizar os chamados PdfContentBytes. O ContentByte do documento está disponível através da propriedade DirectContent da instância de PdfWriter que criamos anteriormente.
Com o PdfContentByte, vamos primeiramente selecionar um retângulo. Após isso, chamamos o método “Stroke” para desenharmos uma linha sem preenchimento na última área selecionada. Dessa forma, esse seria o código para desenharmos o retângulo e o círculo abaixo dos nossos textos:
Um negócio muito bizarro que eu achei na biblioteca iTextSharp é que a coordenada zero do eixo Y fica na parte de baixo da página! Ou seja, a coordenada 0, 0 de uma página no iTextSharp fica no canto inferior esquerdo, e não no canto superior esquerdo (como é o caso do PDFSharp, que faz muito mais sentido).
Finalmente, para adicionarmos a nossa imagem de pinguim no PDF, também temos que utilizar o PdfContentByte. Porém, dessa vez temos que chamar o método “AddImage“. É importante notar que antes de adicionar a imagem, temos que escalá-la do tamanho que queremos (senão a imagem será adicionada com o tamanho original em pixels) e também temos que configurar a posição que queremos adicioná-la na página. Fazemos isso com os métodos “ScaleToFit” e “SetAbsolutePosition“:
Veja só o código completo e o resultado final no Adobe Acrobat Reader:
using (var fileStream = new System.IO.FileStream("output.pdf", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
{
var document = new iTextSharp.text.Document();
var pdfWriter = iTextSharp.text.pdf.PdfWriter.GetInstance(document, fileStream);
document.Open();
iTextSharp.text.FontFactory.RegisterDirectory("C:\\WINDOWS\\Fonts");
var font = iTextSharp.text.FontFactory.GetFont("Calibri", 14);
// Textos.
var paragraph = new iTextSharp.text.Paragraph("Aqui temos um parágrafo alinhado à esquerda.", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_LEFT;
document.Add(paragraph);
paragraph = new iTextSharp.text.Paragraph("Aqui temos um parágrafo centralizado.", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_CENTER;
document.Add(paragraph);
paragraph = new iTextSharp.text.Paragraph("Aqui temos um parágrafo alinhado à direita.", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_RIGHT;
document.Add(paragraph);
paragraph = new iTextSharp.text.Paragraph("E, finalmente, aqui temos um parágrafo justificado. Aqui precisamos de mais texto para ver se o texto está realmente sendo justificado! Confira as outras categorias do meu site para artigos relacionados a outras tecnologias de desenvolvimento de software!", font);
paragraph.Alignment = iTextSharp.text.Element.ALIGN_JUSTIFIED;
document.Add(paragraph);
// Figuras geométricas.
var contentByte = pdfWriter.DirectContent;
contentByte.Rectangle(100, 530, 180, 110);
contentByte.Stroke();
contentByte.Circle(400, 580, 50);
contentByte.Stroke();
// Imagem.
var image = iTextSharp.text.Image.GetInstance("penguins.jpg");
image.ScaleToFit(400, 300);
image.SetAbsolutePosition(80, 200);
contentByte.AddImage(image);
document.Close();
System.Diagnostics.Process.Start("output.pdf");
}
Para mais exemplos de geração de PDFs com a biblioteca iTextSharp, confira estes links:
Atenção para o licenciamento da biblioteca iTextSharp!
Antes de sair utilizando a biblioteca iTextSharp em qualquer projeto, atente-se para o fato que as últimas versões dessa biblioteca utilizam a licença AGPL. E o que isso significa? Significa que ela é open source e que você pode utilizá-la inclusive em projetos comerciais, porém, com um detalhe muito importante: desde que você também distribua a sua aplicação utilizando a licença AGPL! Ou seja, ao utilizar o iTextSharp na sua aplicação comercial, você também terá que distribuí-la como open source!
Se você não quiser distribuir a sua aplicação com a licença AGPL, você tem duas opções. A primeira delas é pagar por uma licença comercial do iText (que não é barato – bagatela de mais de mil dólares atualmente). E a segunda alternativa é utilizar uma versão mais antiga da biblioteca iTextSharp de quando ela ainda não tinha adotado esse tipo de licença. Você consegue encontrar essa variante da biblioteca no NuGet facilmente:
Qual é a melhor opção?
Enfim, agora que já vimos os detalhes de cada uma das duas bibliotecas, vou dar a minha opinião sobre qual você deve utilizar.
Caso você não tenha que trabalhar com textos (ou se os textos do seu PDF tiverem que ser posicionados em lugares específicos e pré-definidos), eu sugiro que você utilize a biblioteca PDFSharp. A sintaxe é muito mais tranquila e você terá que fazer muito menos gambiarras para conseguir implementar as principais funcionalidades de arquivos PDF.
Porém, caso os seus documentos sejam carregados com textos, parágrafos, quebras de página e coisas do tipo, eu sugiro que você parta para a biblioteca iTextSharp. Implementar quebras de página com texto no PDFSharp é aparentemente bem difícil, e o iTextSharp lida com esse tipo de detalhe automaticamente.
Conversão de DOCX ou HTML para PDF?
Uma necessidade que você pode se deparar nos seus aplicativos é ter que converter DOCX ou HTML para PDF. Nenhuma dessas duas bibliotecas suportam essa funcionalidade nativamente, mas, pessoas da comunidade já desenvolveram add-ins que implementam a conversão de HTML para PDF. Você encontra esses add-ins no NuGet com os nomes “HTML Renderer for PDF using PDFSharp” e “iTextSharp XML Worker“.
Já quanto a conversão de DOCX para PDF, pelas minhas pesquisas, você terá que utilizar algum componente comercial. Estes são os links que eu encontrei sobre isso:
Por fim, caso você esteja procurando por um controle para exibir PDFs nas suas aplicações Windows Forms, na empresa onde eu trabalho tivemos exatamente essa necessidade uns tempos atrás. Depois de muita pesquisa e investigação com componentes open source e comerciais, chegamos à conclusão que o componente mais em conta e que dá conta do recado é este aqui:
Não tivemos sorte com os componentes open source nos nossos testes. Se você tiver conseguido utilizar algum componente mais barato para solucionar essa necessidade, por favor, me avise nos comentários.
Concluindo
Ufa, esse foi um artigo longo, hein? Mas, pelo menos eu detalhei de uma vez por todas o procedimento para geração manual de PDFs em aplicativos desenvolvidos com o .NET Framework. Você conferiu a geração de um PDF com texto, figuras geométricas e imagens utilizando tanto a biblioteca PDFSharp quanto a biblioteca iTextSharp. Finalmente, você viu também a minha opinião sobre qual dessas bibliotecas utilizar dependendo do cenário da sua aplicação.
E você, já precisou gerar PDFs manualmente nos seus aplicativos? Caso positivo, você utilizou uma dessas duas bibliotecas? Qual delas? Me conte nos comentários como foi a sua experiência. E, caso negativo, me conte também nos comentários o que você achou desse artigo, OK?
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se utilizando o formulário logo abaixo.
Com as resoluções dos monitores aumentando a cada dia, muitas vezes é difícil do usuário conseguir ler algumas partes do nosso aplicativo ao utilizar uma resolução muito alta.
Ao contrário do WPF, no Windows Forms isso é muito comum, uma vez que a maioria dos controles não é “DPI aware” (ou seja, seu tamanho não se ajusta automaticamente ao aumentarmos ou diminuirmos o DPI).
No artigo de hoje eu vou mostrar para você como é fácil alterar a fonte da mensagem de erro do Report Viewer.
Essa dúvida veio do meu amigo Bruno Maestro (que também é Microsoft MVP, confiram o site dele aqui). Ele queria saber como aumentar a fonte da mensagem de erro do controle do Report Viewer, porque com resoluções muito altas, fica difícil de conseguir ler. Então, vamos ver como podemos fazer?
Alterando a fonte
Antes de tudo, se você não sabe de qual mensagem de erro eu estou falando, ela é aquela mensagem que aparece dentro do próprio controle do Report Viewer quando algo está errado. Para reproduzir esse cenário, simplesmente adicione um controle do Report Viewer no seu formulário e execute a aplicação. Dessa forma, uma mensagem de erro será exibida falando que a definição do relatório não foi especificada:
The source of the report definition has not been specified
E como é que conseguimos alterar a fonte dessa mensagem de erro? Simples, altere a propriedade “Font” do controle do Report Viewer!
Isso fará com que essa nova fonte seja utilizada na mensagem de erro do relatório:
Não se preocupe, as fontes do seu relatório em si não serão alteradas (o Report Viewer obedecerá o que estiver configurado na definição do relatório). Somente a fonte dessa mensagem de erro é que será alterada.
Exibindo uma MessageBox com o erro
Uma alternativa que o Bruno Maestro tinha encontrado foi mostrar uma MessageBox com a mensagem de erro, ao invés de deixar a mensagem de erro ser exibida pelo controle do Report Viewer. Como podemos fazer isso? Simples, é só utilizar o evento ReportError:
Como o próprio nome diz, esse evento é disparado sempre que algum erro acontece no controle do Report Viewer. E todas as informações do erro podem ser acessadas através do ReportErrorEventArgs disponibilizado por esse evento. Dessa forma, conseguimos facilmente exibir uma MessageBox com a mensagem de erro:
Além disso, podemos ainda utilizar a propriedade “Handled” do ReportErrorEventArgs se quisermos que a mensagem de erro seja exibida somente no nosso MessageBox (e não no controle do Report Viewer). Para isso, basta configurar essa propriedade como “true“:
Com isso, a mensagem de erro será exibida na nossa MessageBox e o controle do Report Viewer continuará em branco (sem a mensagem de erro):
Concluindo
Sempre devemos estar atentos à acessibilidade dos nossos aplicativos. Um formulário com fontes muito pequenas pode dificultar a vida de quem não enxerga direito ou de quem está utilizando uma resolução muito alta.
Como o controle do Report Viewer (desktop) utiliza a engine do Windows Forms, ele não é “DPI aware” e o resultado dessa deficiência é que a fonte da mensagem de erro pode acabar ficando muito pequena.
No artigo de hoje você aprendeu a contornar esse problema de duas formas. A primeira delas foi alterando a própria fonte da mensagem de erro do Report Viewer. E a segunda maneira foi exibindo a mensagem de erro em uma MessageBox.
Agora pare e pense: os seus aplicativos que utilizam o controle do Report Viewer estão rodando em monitores grandes que têm uma possibilidade de resolução alta? Caso positivo, sugiro que você dê uma revisitada nesses aplicativos para conferir se o tamanho da mensagem de erro está grande o suficiente. Me conte depois nos comentários qual foi o resultado, OK?
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 logo abaixo.
Já faz algum tempo que as versões do controle de visualização de relatórios do Crystal Reports para o .NET Framework vêm com uma tab chamada “Main Report“. Essa tab serve para controlar o nível de drill down do relatório. Ou seja, quando temos um sub-relatório, o usuário pode fazer um drill-down no sub-relatório (entrar no sub-relatório), e então essa tab serve justamente para que o usuário consiga voltar ao relatório principal.
Porém, por um motivo ou outro, pode ser que nós queiramos desabilitar a opção de drill-down no nosso relatório. E como é que nós escondemos essa tab Main Report no Crystal Reports? Essa tarefa, que não é tão trivial, é o tema do artigo de hoje.
O que é a tab “Main Report”?
Como já mencionei anteriormente na introdução, a tab “Main Report” serve para controlar o drill-down nos nossos relatórios com o Crystal Reports. Ela é aquela tab estranha que fica logo abaixo da toolbar principal do controle visualizador do Crystal Reports:
Em relatórios que não têm sub-relatórios, não faz o menor sentido exibirmos essa barra. Ela pode, inclusive, acabar confundindo o usuário, que pode pensar que ela serve para outra coisa. Além disso, pode ser que nós desejemos desabilitar completamente o drill-down, mesmo que o relatório tenha sub-relatórios.
Para desabilitar o drill-down, podemos simplesmente configurar a propriedade “EnableDrillDown” como “False“:
Porém, isso não é o suficiente para que a tab “Main Report” desapareça. No caso do controle para Windows Forms, temos que fazer uma pequena “maracutaia“.
Escondendo a tab “Main Report”
Surpreendentemente, no controle do Crystal Reports do Windows Forms, não existe uma propriedade para desabilitarmos a tab “Main Report“. Dessa forma, a única maneira de escondê-la é percorrermos os controles internos do Crystal Reports até encontrarmos a maldita barra para que possamos ocultá-la.
Para isso, vamos criar um método que receberá um CrystalReportViewer e que será justamente responsável por fazer essa “gambiarra“. Dentro desse método, percorreremos todos os controles filhos do CrystalReportViewer até encontrarmos um PageView (que é onde a tab está localizada). Dentro do PageView, pegaremos o primeiro controle, que deverá ser um TabControl. Esse é justamente o TabControl que controla o esquema de drill-down.
Porém, não sei se você sabe, mas, o TabControl do Windows Forms não permite que escondamos o seu cabeçalho. Ou seja, a única opção para escondermos o cabeçalho do TabControl é configurarmos o seu tamanho para algo muito pequeno.
Veja só como fica o código no final das contas:
private void HideCrystalReportViewerMainTab(CrystalDecisions.Windows.Forms.CrystalReportViewer viewer)
{
foreach (var control in viewer.Controls)
{
var pageView = control as CrystalDecisions.Windows.Forms.PageView;
if (pageView != null && pageView.Controls.Count > 0)
{
var tab = pageView.Controls[0] as TabControl;
if (tab != null)
{
tab.ItemSize = new Size(0, 1);
tab.SizeMode = TabSizeMode.Fixed;
}
}
}
}
Feito isso, basta irmos no construtor do formulário onde o controle do Crystal Reports está posicionado e chamarmos o nosso método passando o controle:
public FormOndeEstaOControleDoCrystalReports()
{
InitializeComponent();
HideCrystalReportViewerMainTab(crystalReportViewer);
}
E o resultado é que a tab “Main Reports” sumirá de uma vez por todas:
Obviamente, como este código pode ser reutilizado em todos os formulários, sugiro que você o coloque como método estático em alguma classe de métodos utilitários do seu projeto.
Sei que esse código não é o melhor do mundo e que ele provavelmente quebrará caso a SAP decida alterar a estrutura interna do controle, mas, infelizmente é a única forma de escondermos essa tab na versão atual, já que a própria SAP não disponibilizou uma maneira de fazermos isso nativamente.
E no controle web?
Por outro lado, no controle web, a SAP disponibilizou uma propriedade que podemos utilizar para esconder a tab “Main Report“. Ela é a propriedade chamada “HasDrilldownTabs“:
Confira o antes:
E o depois:
Disclaimer
A solução que eu utilizei neste artigo para esconder a tab “Main Report” do Crystal Reports foi baseada nesta discussão nos fóruns da MSDN americana (eu me baseei nesta discussão, mas, repare que a minha solução final está um pouco diferente do sugerido nesta discussão):
Muitas vezes o excesso de opções acaba confundindo o usuário da nossa aplicação. Esse pode ser o caso da tab “Main Report” do controle do Crystal Reports, principalmente se você não utiliza sub-relatórios ou drilldown nos seus relatórios. Como não é possível escondermos essa tab de forma nativa, neste artigo eu mostrei para você uma forma de escondê-lo, caso você sinta que isso é necessário.
Apesar de ser uma pequena “gambiarra“, infelizmente é a única maneira que temos para realizar essa tarefa. No controle web do Crystal Reports, a SAP disponibilizou uma propriedade para escondermos essa tab, mas, no controle desktop parece que ela se esqueceu.
Me conte depois nos comentários se você já tinha reparado nessa tab e se os usuários da sua aplicação já ficaram confusos com ela.
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se utilizando o formulário logo abaixo.
Para quem não sabe, a minha expertise é o desenvolvimento de aplicações desktop utilizando a plataforma da Microsoft. Isso quer dizer que toda a minha carreira até agora foi focada em desenvolvimento desktop. E isso quer dizer também que eu sou uma verdadeira porta no que se diz respeito a desenvolvimento web.
Porém, como um dos temas mais requisitados na minha pesquisa de conteúdo do ano passado foi desenvolvimento web, vou tentar abordar uma coisa ou outra utilizando a plataforma de desenvolvimento web da Microsoft, na medida do possível. Outro tema que foi altamente requisitado diz respeito à geração de relatórios. Dessa forma, vou juntar esses dois temas neste artigo, respondendo a uma pergunta que já me fizeram mais de uma vez: como utilizar o Report Viewer no ASP.NET MVC?
Amigo(a) desenvolvedor(a) web: tenho que te dar os meus parabéns. Como eu mencionei na introdução, a minha experiência com desenvolvimento web é praticamente nula. Dessa forma, para escrever esse artigo eu precisei de uma certa quantidade de pesquisa, tentativa e erro. Algo que eu notei em praticamente todas as minhas fontes de pesquisa é que elas são incompletas e, o pior, consideram que a pessoa que está lendo o artigo é um expert no assunto.
Enfim, depois de lutar um bocado, consegui encontrar duas maneiras de exibir relatórios do Report Viewer no ASP.NET MVC.
Na primeira delas nós jogamos um pouco “fora das regras” e simplesmente criamos um web form no nosso projeto MVC, o que faz com que o projeto se torne um híbrido de MVC e web forms.
Já na segunda alternativa, ficamos “100% no MVC” utilizando a biblioteca Report Viewer for MVC (entre aspas porque no final das contas a biblioteca utiliza web forms por trás dos panos).
Preparando o relatório a ser exibido
Antes de mostrar as duas alternativas, vamos criar o relatório que exibiremos no nosso projeto do ASP.NET MVC. Como o foco deste artigo não é o relatório em si, mas sim, como exibi-lo, vamos criar um relatório extremamente simples só para termos uma ideia de como prosseguir.
Primeiramente, vamos criar um novo projeto do tipo “ASP.NET Web Application” escolhendo o template do MVC:
Uma vez que o Visual Studio tenha criado o projeto, vamos adicionar um DataSet tipado que servirá como fonte de dados do nosso relatório. Adicione o DataSet na pasta “Models” utilizando o nome “PessoaDataSet“:
Dentro do DataSet, adicione uma DataTable chamada “Pessoa” com os campos Id (inteiro, auto incremento, chave primária), Nome e Sobrenome (strings):
Feito isso, crie agora uma pasta chamada “Reports” dentro do projeto e adicione um novo item do tipo “Report“, dando o nome de “RelatorioListaPessoas“:
Dentro do relatório, adicione uma tabela e configure o seu DataSet da seguinte maneira:
Adicione os campos do DataSet na tabela e formate o relatório como você bem desejar, lembrando que o foco deste artigo não é a construção do relatório, mas sim, a sua exibição em um projeto do tipo ASP.NET MVC. Veja só como ficou o meu relatório após a formatação:
Opção 1 – Adicionando um web form com o controle do Report Viewer
Agora que já temos um relatório prontinho para ser exibido, vamos conferir a primeira maneira que podemos utilizar para exibi-lo em um projeto ASP.NET MVC, que é criando um web form.
Primeiramente, temos que adicionar a referência ao controle do Report Viewer. Para isso, vá até a tela de adição de referências e marque a opção “Microsoft.ReportViewer.WebForms“, dentro da categoria Assemblies / Extensions:
Agora clique na nova referência que foi adicionada, vá até a janela de propriedades e altere o tipo de “Copy Local” para “true“:
Feito isso, adicione um web form no projeto, dando o nome de “RelatorioListaPessoas“:
Com o novo web form aberto, vá até a caixa de ferramentas, encontre o item correspondente ao Report Viewer e arraste-o para dentro da “div“:
[imagem]
Para o nosso código não ficar uma bagunça, altere o ID do Report Viewer para “ReportViewer” (ao invés do default que é “ReportViewer1“):
Perceba que o código é muito simples. Primeiramente criamos uma instância do nosso DataSet e depois configuramos algumas propriedades no nosso controle do Report Viewer. Nada muito sofisticado, não é mesmo? Vamos ver se funciona?
Compile o projeto e escolha a opção de ver essa página no browser:
E, no meu caso, não funcionou:
The base class includes the field ‘ReportViewer’, but its type (Microsoft.Reporting.WebForms.ReportViewer) is not compatible with the type of control (Microsoft.Reporting.WebForms.ReportViewer).
Você sabe por que tivemos esse problema? Porque a referência que adicionamos aponta para a versão 10 do Report Viewer e o controle adicionado pela caixa de ferramentas aponta para a versão 11 do Report Viewer. Para consertarmos esse problema, temos que remover a referência anterior para então adicionarmos a referência correta (apontando para a versão 11 do Report Viewer – ah, e não esqueça de configurar “Copy Local” = “true” mais uma vez!). A dll da versão 11 está disponível no GAC (C:\Windows\assembly\GAC_MSIL):
Ao tentarmos abrir a página normalmente, receberemos outro erro (como o mundo do desenvolvimento web é cheio de artimanhas, não?):
The Report Viewer Web Control requires a System.Web.UI.ScriptManager on the web form.
Esse erro é fácil de consertar. Como a própria mensagem de erro diz, temos que adicionar um ScriptManager no web form. Basta encontra-lo na caixa de ferramentas (dentro da seção “AJAX Extensions“) e arrasta-lo para dentro da “div“, uma linha acima do Report Viewer:
Será que depois disso o relatório será exibido sem nenhum erro? É claro que não! Temos que consertar mais um erro, veja só:
The Report Viewer Web Control HTTP Handler has not been registered in the application’s web.config file.
Para consertar esse último erro, temos que ir até o web.config para adicionarmos algumas linhas, mais especificamente, dentro da tag “system.webserver” temos que adicionar as seguintes linhas:
E aí sim, finalmente, o nosso relatório será exibido com sucesso:
Antes de mostrar como exibir o relatório com a biblioteca ReportViewer for MVC, você notou que o controle está com um tamanho fixo e, dessa forma, o relatório não cabe sem que barras de rolagem sejam exibidas? É muito fácil configurarmos o controle do Report Viewer para que ele ajuste o seu tamanho automaticamente de acordo com o tamanho do relatório. Para isso, basta adicionarmos três linhas no nosso code-behind, logo antes de chamarmos o método Refresh:
E com isso o tamanho do controle será ajustado automaticamente dependendo do tamanho do relatório:
Opção 2 – Utilizando a biblioteca ReportViewer for MVC
Agora que já vimos como exibirmos relatórios do Report Viewer em projetos MVC através da criação de um web form, vamos ver como podemos automatizar isso através de uma biblioteca?
Feito isso, adicione um novo Controller na pasta “Controllers“, escolhendo o template “MVC 5 Controller – Empty” e dando o nome de “RelatorioListaPessoaController“. Uma vez que o Visual Studio tenha criado o Controller, adicione o seguinte código do método Index:
public ActionResult Index()
{
var pessoaDataSet = new Models.PessoaDataSet();
pessoaDataSet.Pessoa.AddPessoaRow("André", "Lima");
pessoaDataSet.Pessoa.AddPessoaRow("Larissa", "Lima");
pessoaDataSet.Pessoa.AddPessoaRow("Sophie", "Lima");
var viewer = new Microsoft.Reporting.WebForms.ReportViewer();
viewer.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local;
viewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + @"Reports\RelatorioListaPessoas.rdlc";
viewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource("PessoaDataSet", (System.Data.DataTable)pessoaDataSet.Pessoa));
viewer.SizeToReportContent = true;
viewer.Width = System.Web.UI.WebControls.Unit.Percentage(100);
viewer.Height = System.Web.UI.WebControls.Unit.Percentage(100);
ViewBag.ReportViewer = viewer;
return View();
}
Note que o código é extremamente parecido com o código do code-behind apresentado na primeira alternativa. Basicamente criamos uma instância do DataSet, criamos uma nova instância do controle do ReportViewer e configuramos essa instância. A principal diferença é que configuramos uma propriedade chamada “ReportViewer” na ViewBag apontando para o controle que acabamos de criar.
Com isso, podemos agora criar uma nova View que fará a exibição do relatório em si. Para isso, adicione uma nova View na pasta “RelatorioListaPessoa” (que foi criada automaticamente pelo Visual Studio ao adicionarmos o Controller), dando no nome de “Index“.
Dentro da View, adicione a seguinte linha logo no topo do arquivo:
@using ReportViewerForMvc;
Depois, no final do arquivo, adicione o seguinte código:
@Html.ReportViewer(ViewBag.ReportViewer as Microsoft.Reporting.WebForms.ReportViewer)
Perceba que o que fizemos aqui foi basicamente adicionarmos uma “referência” aos métodos de extensão da biblioteca (cláusula “using” que adicionamos no início do arquivo). E depois utilizamos o método de extensão “ReportViewer” (disponibilizado pela biblioteca) para exibirmos o relatório que está armazenado na propriedade “ReportViewer” da ViewBag. Bem simples, não é mesmo? Veja só o resultado ao escolhermos a opção “View in browser” dessa View:
Concluindo
Ao trabalharmos com projetos do tipo ASP.NET MVC, temos duas opções para exibirmos relatórios do Report Viewer: a primeira delas é “na mão“, criando um web form com o controle do Report Viewer, e a segunda delas é utilizando a biblioteca ReportViewer for MVC.
Neste artigo você conferiu detalhadamente essas duas maneiras de exibir seus relatórios do Report Viewer em projetos ASP.NET MVC. Eu particularmente prefiro muito mais utilizar a biblioteca ReportViewer for MVC, uma vez que ele já configura o projeto, poupando muito do trabalho que teremos ao tentarmos exibir os relatórios “na mão“. Além disso, o código fica muito mais limpo também.
E você, já precisou exibir relatórios do Report Viewer em projetos ASP.NET MVC? Qual dessas duas modalidades você utilizou? Ou talvez você utilizou algo completamente diferente? Conte nos comentários como é que foi a sua experiência.
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 logo abaixo.
Um tempo atrás recebi um comentário muito interessante no meu artigo sobre o Crystal Reports no Visual Studio 2013. O Paulo César Lopes de Sousa, que é leitor do meu site, estava com um problema. Basicamente, o Crystal Reports parou de funcionar depois de atualizar para o Windows 10. Depois de muita tentativa e erro, conseguimos resolver o problema. E é justamente a solução para esse problema que eu vou apresentar no artigo de hoje.
Entendendo o problema
Imagine o seguinte cenário: você está com o Visual Studio 2013 instalado no Windows 8.1 utilizando o Crystal Reports for Visual Studio SP 14. Aí você decide, finalmente, atualizar para o Windows 10 (ou melhor, você não aguentava mais o Windows falando todo dia para você “Atualiza aí para o Windows 10, por favor, atualiza aí vai, não custa nada, vai atualizar?“).
Ao abrir os seus projetos desktop que utilizam o Crystal Reports, tudo continua funcionando que é uma beleza. Porém, sabe aquele projetinho web que também utiliza o Crystal Reports? Vamos ver se ele continua funcionando? Pois é, os relatórios não funcionam mais. No lugar deles, aparece uma página em branco. O que fazer?
Pesquisando a solução
Primeiramente, atualizamos o Crystal Reports para a versão mais nova (SP 15). Porém, nada feito. O resultado continua o mesmo. Páginas em branco ao tentarmos acessar os relatórios do Crystal Reports em qualquer browser que seja.
Qual é a saída agora? Procurar a solução na internet, é claro. Pesquisando por esse problema, acabei chegando em um post no fórum da SAP (atual dona do Crystal Reports):
Porém, tentei seguir as instruções desse artigo e não consegui resolver o problema (e o Paulo César, aquele leitor do meu site, também não conseguiu).
E qual a solução que realmente funciona?
Achei muito estranho porque uma pessoa tinha feito um comentário naquele post do fórum da SAP falando que a solução apresentada tinha funcionado:
Eu postei uma resposta perguntando mais detalhes, mas, obviamente, ele não respondeu. A propósito, pessoal, gostaria de deixar uma pequena nota aqui: se o seu problema foi resolvido com a ajuda de um fórum (ou blog) e depois de um tempo alguém pede ajuda para você sobre o mesmo tópico, por favor, responda a dúvida da pessoa! Retribua a ajuda que te deram, OK?
Enfim, como o usuário não me respondeu, resolvi dar uma olhada mais a fundo no link que ele tinha utilizado para resolver o problema. E não é que, nas entrelinhas, tinha um pequeno detalhe que eu não tinha seguido?
Vou detalhar agora para você a solução que eu utilizei (e que depois o Paulo César acabou confirmando que funcionou no ambiente dele também).
1 – Copie a pasta do Crystal Reports no diretório do seu site
A pasta do Crystal Reports fica localizada em “C:\inetpub\wwwroot\aspnet_client\system_web\4_0_30319“. Você precisa copiar a pasta chamada “crystalreportviewers13” desse diretório para o diretório do seu site (no mesmo nível em que as pastas “bin“, “App_Data“, “App_Start“, etc, ficam).
2 – Adicione o Section Group do Crystal Reports no web.config
Abra o seu arquivo web.config e, dentro da tag “configSections“, adicione o seguinte código:
3 – Adicione a tag “businessObjects” no web.config
Ainda dentro do arquivo web.config, logo após o fechamento da tag “configSections” (ou seja, logo depois do </configSections>), adicione o seguinte código:
E é justamente nesse código que mora o problema. Se olharmos o post que sugere essa solução, bem nas entrelinhas o autor fala que esse código em alguns casos funciona com um “~” e em outros casos funciona sem o “~“:
Ou seja, essa solução exatamente como eu apresentei aqui funcionou no meu cenário e também no cenário do Paulo César. Porém, caso ela não funcione no seu cenário, tente adicionar o “til” na linha correspondente ao ResourceUri:
No meu caso só funcionou ao remover o “til“, mas, nunca se sabe, né?
Concluindo
Esse artigo, além de explicar passo a passo como resolver a situação em que o Crystal Reports para de funcionar ao atualizarmos para o Windows 10, ele também é uma lição de que devemos ler detalhadamente as instruções dos artigos. Muitas vezes a solução para o nosso problema pode estar nas entrelinhas.
E você, também passou por esse problema? Resolveu dessa forma? Deixe-nos a sua experiência nos comentários.
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se utilizando o formulário logo abaixo.
Não é segredo para ninguém que eu acho o Report Viewer uma ferramenta muito interessante para a geração de relatórios em aplicativos desenvolvidos com o .NET Framework. Eu já escrevi muitos artigos sobre o Report Viewer e inclusive já compilei uma grande parte do meu conhecimento em um e-book sobre essa ferramenta.
Entretanto, a grande maioria dos artigos sobre o Report Viewer só mostra a ligação de dados através de um DataSet tipado. E como é que fica o pessoal que utiliza o Entity Framework? Você já deve ter ouvido falar desse ORM da Microsoft, não é mesmo?
Os poucos artigos (em inglês) que mostram a utilização do Report Viewer com Entity Framework abordam exemplos muito simples, utilizando somente uma entidade. No artigo de hoje vou mostrar para você como alimentar um relatório mestre/detalhe do Report Viewer utilizando uma query customizada entre várias tabelas vindas do Entity Framework.
Criando o modelo de dados
Para montarmos um relatório mestre/detalhe, normalmente precisamos de uma hierarquia de tabelas no nosso modelo de dados. Resolvi escolher um exemplo clássico com múltiplas tabelas, que é modelo de gerenciamento de pedidos.
Nesse modelo, temos uma classe Pedido e uma classe ItemPedido. Cada pedido tem um ou mais itens de pedido. Além disso, o pedido está vinculado a um cliente e o item de pedido está vinculado a um produto. Confira no diagrama abaixo o relacionamento entre essas classes:
O exemplo desse artigo utilizará um projeto do tipo “Windows Forms Application“. Dito isso, a primeira coisa que temos que fazer é criar um projeto desse tipo.
Uma vez criado o projeto, vamos adicionar a referência ao Entity Framework 6, utilizando o NuGet. Para isso, vá até a tela de gerenciamento de pacotes do NuGet e escolha a opção para instalar o pacote do Entity Framework (que normalmente é o primeiro da lista):
Com o Entity Framework instalado no nosso projeto, podemos partir para a criação das nossas classes de Cliente, Produto, Pedido e ItemPedido. Adicionaremos essas classes dentro de uma nova pasta no nosso projeto, chamada “Models“:
Veja a seguir o código de cada uma dessas classes:
public class Cliente
{
[System.ComponentModel.DataAnnotations.Key]
[System.ComponentModel.DataAnnotations.Schema.DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ClienteId { get; set; }
public string Nome { get; set; }
}
public class Produto
{
[System.ComponentModel.DataAnnotations.Key]
[System.ComponentModel.DataAnnotations.Schema.DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ProdutoId { get; set; }
public string Descricao { get; set; }
public double Preco { get; set; }
}
public class Pedido
{
[System.ComponentModel.DataAnnotations.Key]
[System.ComponentModel.DataAnnotations.Schema.DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int PedidoId { get; set; }
public int ClienteId { get; set; }
public virtual Cliente Cliente { get; set; }
public DateTime DataPedido { get; set; }
public virtual ICollection<ItemPedido> ItensPedido { get; set; }
}
public class ItemPedido
{
[System.ComponentModel.DataAnnotations.Key]
[System.ComponentModel.DataAnnotations.Schema.DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ItemPedidoId { get; set; }
public int PedidoId { get; set; }
public virtual Pedido Pedido { get; set; }
public int ProdutoId { get; set; }
public virtual Produto Produto { get; set; }
public double Quantidade { get; set; }
public double ValorTotal { get; set; }
}
Note que a estrutura dessas classes é bem simples, uma vez que o foco do artigo não é criar uma super-estrutura de dados, mas sim, mostrar como podemos utilizar essa estrutura de múltiplas classes para alimentar o nosso relatório do Report Viewer. Também não vou entrar nos detalhes do Entity Framework neste artigo, uma vez que já existem inúmeros artigos sobre esse tema.
Após adicionarmos as classes do modelo, temos que criar uma classe de contexto do Entity Framework. Para isso, crie uma nova classe no projeto, chamada “Contexto“, que deverá ter o seguinte conteúdo:
public class Contexto : System.Data.Entity.DbContext
{
public System.Data.Entity.DbSet<Models.Cliente> Clientes { get; set; }
public System.Data.Entity.DbSet<Models.Produto> Produtos { get; set; }
public System.Data.Entity.DbSet<Models.Pedido> Pedidos { get; set; }
public System.Data.Entity.DbSet<Models.ItemPedido> ItensPedido { get; set; }
}
Finalmente, vamos até o code-behind do formulário que o Visual Studio criou automaticamente com o projeto Windows Forms para adicionarmos o código que populará alguns dados em cada uma das tabelas (Cliente, Produto, Pedido e ItensPedido):
Pronto. Com isso temos o modelo criado e o contexto inicializado com alguns dados. Se observarmos o banco de dados, veremos que o Entity Framework terá criado as tabelas correspondentes a cada classe do nosso modelo:
Ajustando o formulário de preview
Agora que já temos o banco de dados, vamos preparar os dados que deverão ser exibidos no relatório. Como queremos fazer um relatório mestre/detalhe (com as informações do pedido no mestre e as informações dos itens de pedido no detalhe), temos duas opções: ou trabalhamos com agrupamentos (agrupando os dados pelo ID do Pedido) ou trabalhamos com sub-relatórios.
Para trabalharmos com agrupamentos, precisamos passar para o relatório todos os dados de forma “desnormalizada” em uma fonte de dados única (onde cada linha da fonte de dados teria as informações de um item de pedido, juntamente com todas as informações do pedido correspondente).
Por outro lado, se quisermos trabalhar com sub-relatórios, teríamos que passar duas fontes de dados para o relatório: uma representando os pedidos e outra representando os itens de pedido.
Quando me deparo com essa situação, normalmente eu escolho o primeiro caminho (agrupamentos), uma vez que o relatório fica muito mais simples de ser gerado e também tem o fato de agrupamentos terem uma performance melhor que sub-relatórios no Report Viewer.
Dessa forma, nesse artigo eu vou mostrar somente como fazer esse relatório utilizando agrupamentos. Caso você opte por utilizar sub-relatórios, tenho certeza que você conseguirá adaptar o exemplo sem muitas dificuldades.
O grande problema das fontes de dados do Report Viewer é o fato de que não é possível criarmos uma fonte de dados dinâmica. O que eu quero dizer com isso é que não é possível adicionarmos uma fonte de dados no relatório e especificarmos manualmente as colunas dessa fonte de dados. Toda fonte de dados do Report Viewer deve ser baseada em um banco de dados, serviço, objeto ou lista do SharePoint.
Por causa disso, antes de prosseguirmos, teremos que criar uma nova classe no nosso projeto que servirá de representação para a estrutura de dados que alimentará o relatório. Para isso, crie uma nova pasta no projeto (chamada “Report“) e adicione uma nova classe chamada “DadosRelatorio“:
public class DadosRelatorio
{
public int PedidoId { get; set; }
public DateTime DataPedido { get; set; }
public int ClienteId { get; set; }
public string NomeCliente { get; set; }
public int ProdutoId { get; set; }
public string DescricaoProduto { get; set; }
public double PrecoProduto { get; set; }
public double Quantidade { get; set; }
public double ValorTotal { get; set; }
}
Note que essa classe possui basicamente todas as propriedades das quatro classes que criamos anteriormente. Em outras palavras, ela representa um item de pedido, juntamente com todas as outras propriedades relacionadas (descrição do produto, preço, nome do cliente, etc).
No caso desse exemplo, teremos somente um relatório, por isso dei o nome de “DadosRelatorio” para essa classe. Em um sistema verdadeiro, provavelmente você terá múltiplos relatórios, portanto, caso os seus relatórios sejam baseados em mais de uma tabela, você terá que criar múltiplas classes desse tipo (por exemplo, “DadosRelatorioFornecedor“, “DadosRelatorioFatura“, etc).
Uma vez tendo criado a classe dos dados do relatório, vamos até o design do formulário para adicionarmos um controle do Report Viewer (dê o nome de “reportViewer” para esse controle e fixe-o para que ele ocupe o tamanho todo do formulário):
Antes de continuar com a próxima seção deste artigo, compile o projeto. Se você não compilar o projeto, existe a chance que o Report Viewer não reconheça a classe “DadosRelatorio” que criamos anteriormente.
Desenhando o relatório
Com o formulário de preview preparado para a exibição do relatório, agora só falta o mais importante: o relatório em si! Para resolver esse problema, vamos adicionar um novo relatório chamado “RelatorioPedido” na pasta “Report“.
A primeira coisa que vamos fazer após termos adicionado o relatório é criar a fonte de dados. Para isso, vá até a janela de dados do relatório (“Report Data“), clique com o botão direito em “Datasets” e escolha a opção “Add Dataset“:
Na tela de escolha do tipo de Dataset, escolha a opção “Object” e clique em “Next“:
Feito isso, na tela de escolha do tipo do objeto, expanda o namespace do seu projeto e encontre a classe “DadosRelatorio” que criamos anteriormente e clique em “Finish“:
Nota: caso a classe “DadosRelatorio” não apareça na lista, é porque você não seguiu as instruções e esqueceu de compilar o projeto antes de prosseguir. Dessa forma, cancele a operação, compile o projeto e repita os passos apresentados acima.
Finalmente, dê o nome de “DadosRelatorio” para o Dataset que será criado e clique em “OK“:
Agora que já temos a fonte de dados preparada, adicione um componente do tipo “Table” no relatório e configure o seu Dataset (nas propriedades do componente) apontando para o Dataset “DadosRelatorio” que criamos anteriormente:
Feito isso, vamos adicionar um grupo pelo ID do Pedido. Para isso, vá até “Row Groups” e escolha a opção “Add Group / Parent Group“. Não esqueça de marcar as opções para gerar o cabeçalho e rodapé do grupo. Isso facilitará bastante o ajuste do layout da tabela:
Outra configuração interessante que podemos ajustar no grupo é a quebra de página. Se você quiser que cada pedido fique em uma página diferente, vá até as propriedades do grupo e marque a opção “Between each instance of a group” na categoria de “Page Breaks“:
Após isso, o próximo passo que temos que seguir é deletarmos a coluna que foi criada anteriormente e ajustarmos o layout da tabela:
Se você quiser, você pode adicionar um cabeçalho no relatório com o título “Pedido“, ou até mesmo com o logotipo da sua empresa ou cliente:
Ajustes finais no formulário de preview
Você está preparado(a) para a parte final? Agora que já temos o nosso relatório desenhado, a única coisa que está faltando é escolhermos esse relatório no controle do Report Viewer e popularmos o relatório com os dados vindos do nosso contexto do Entity Framework.
Vá até o designer do formulário e escolha o relatório que criamos anteriormente:
Feito isso, vamos até o code-behind e, no evento “Load” do formulário, vamos criar um “IEnumerable de DadosRelatorio” pegando os dados de todas as tabelas envolvidas com os itens de pedido através de uma LINQ query. Com o resultado em mãos, basta adicionarmos o resultado da LINQ query como fonte de dados do relatório:
O Report Viewer é uma excelente ferramenta para geração de relatórios disponibilizada diretamente pela Microsoft. Em uma outra categoria completamente diferente, o Entity Framework é um ORM muito poderoso, também disponibilizado diretamente pela Microsoft.
Nesse artigo você aprendeu a utilizar de forma combinada essas duas ferramentas importantíssimas que deve estar presente na caixa de ferramentas de qualquer desenvolvedor de aplicações desktop na plataforma Microsoft.
E você, já precisou popular relatórios do Report Viewer com Entity Framework? Você seguiu essa metodologia? Deixe a sua opinião nos comentários!
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se utilizando o formulário logo abaixo.
Toda ferramenta de geração de relatórios possibilita a criação de parâmetros que podem ser passados da aplicação para o relatório. No caso do Crystal Reports, é claro que não poderia ser diferente. A passagem de parâmetros para relatórios do Crystal Reports com C# é extremamente descomplicada. Com simplesmente uma linha de código para cada parâmetro, conseguimos configurar os valores que desejamos passar para o relatório.
No artigo de hoje você verá qual é a utilidade de criarmos parâmetros nos nossos relatórios, como podemos criar parâmetros em relatórios do Crystal Reports e como passar valores para esses parâmetros através de uma aplicação Windows Forms.
Por que utilizar parâmetros?
Antes de demonstrar como podemos utilizar parâmetros com o Crystal Reports, vale a pena conversarmos um pouquinho sobre a utilidade de parâmetros em relatórios. Independentemente da ferramenta que você utilize para gerar os seus relatórios, é sempre interessante lembrar que é possível passarmos valores fixos para o relatório.
Os parâmetros podem ter inúmeras utilidades nos seus relatórios, sendo as principais a filtragem de dados e a customização de layout.
Imagine que você alimenta o seu relatório com dados de vários clientes. Às vezes não é possível alterarmos a forma com que os dados são passados para o relatório (por exemplo, se o relatório é alimentado com o resultado de uma stored procedure). Dessa forma, a única maneira de filtrarmos os dados do relatório é passando um parâmetro para ele. Nesse caso, poderíamos, por exemplo, passar o ID do cliente como parâmetro para o relatório e, em seguida, utilizaríamos o valor desse parâmetro para filtrarmos quais dados serão exibidos no relatório.
Já quanto a questão de customização de layout, imagine que um mesmo relatório seja exibido para todos os usuários do sistema. Porém, somente administradores do sistema podem ver uma determinada área do relatório. Nesse caso, poderíamos criar um parâmetro com o grupo do usuário e utilizaríamos o valor desse parâmetro para exibirmos ou não essa área do relatório.
Criando parâmetros no relatório
Agora que você já entendeu a utilidade dos parâmetros em relatórios, vamos ver como podemos passar parâmetros para os nossos relatórios do Crystal Reports através do C#?
Primeiramente, vamos construir um exemplo absurdamente simples de relatório utilizando um projeto do tipo “Windows Forms Application“. Uma vez criado o projeto, vamos adicionar um novo item do tipo “Crystal Reports” (disponível dentro da categoria “Reporting“). Dê o nome de “RelatorioParametro” para o novo relatório que será criado:
Com o relatório criado, vamos adicionar um novo parâmetro. Fazemos isso através da janela “Field Explorer“, clicando com o botão direito em “Parameter Fields” e escolhendo a opção “New“:
Na janela de configuração do parâmetro, dê o nome de “GrupoUsuario” para o novo parâmetro e escolha o tipo de dados “String“:
Feito isso, arraste o parâmetro para dentro da área de cabeçalho de página do relatório:
Obviamente, em um cenário real, o seu relatório será muito mais complexo. No exemplo deste artigo só arrastamos o parâmetro para dentro do relatório para checarmos se o valor está realmente sendo recebido no relatório. Nos seus relatórios “de verdade“, você pode utilizar os valores do parâmetro para praticamente qualquer coisa, desde fórmulas até expressões de visibilidade dos controles.
Passando valores para os parâmetros
Com o parâmetro criado, vamos testar como ficaria a exibição desse relatório no nosso formulário. Para isso, vamos adicionar um controle do tipo “CrystalReportViewer” no nosso formulário:
Feito isso, na smart tag do controle, escolha a opção “Choose a Crystal Report” e encontre o relatório que criamos anteriormente na lista de opções:
Nota: não esqueça de renomear o relatório, uma vez que ele virá com o nome “RelatorioParametro1“. Eu alterei para “RelatorioParametro” (sem o “1” no final):
O que acontece se rodarmos a aplicação nesse momento? Simples: o Crystal Reports perguntará o valor para o parâmetro diretamente dentro da interface do controle:
Para evitarmos isso, temos que passar o valor desejado para esse parâmetro antes que o relatório seja exibido. Fazemos isso utilizando o método “SetParameterValue“, sendo que o primeiro item que devemos passar para esse método é o nome do parâmetro (“GrupoUsuario“) e o segundo item é o valor do parâmetro (“Admin“):
public FormCrystal()
{
InitializeComponent();
RelatorioParametro.SetParameterValue("GrupoUsuario", "Admin");
}
Execute a aplicação e veja o resultado:
Concluindo
A passagem de parâmetros é um conceito muito importante na geração de relatórios. Eles podem ser utilizados para diversas finalidades, sendo as mais comuns a customização de layout e filtragem de dados.
Nesse artigo você aprendeu a passar parâmetros para relatórios do Crystal Reports com C#. Espero que o tópico tenha ficado claro e que você tenha entendido a utilidade da passagem de parâmetros ao gerarmos relatórios com qualquer ferramenta que seja.
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 logo abaixo.
Quando desenvolvemos relatórios para as nossas aplicações, muitas vezes surge a necessidade de filtrarmos os dados que serão exibidos. Às vezes alguns dados da tabela não fazem sentido no relatório que estamos construindo. Outras vezes, nem todos os usuários têm permissões de ver todos os dados. Existem incontáveis utilidades para a filtragem de dados em relatórios.
Ao trabalharmos com o Report Viewer, temos a possibilidade de filtrarmos os dados diretamente no relatório. Porém, muitas vezes essa não é a estratégia mais recomendada. Dessa forma, nesse artigo nós veremos como passar uma DataTable já filtrada para o Report Viewer (através de uma DataView).
Criando o relatório de exemplo
Para demonstrar as funcionalidades de filtro com o Report Viewer, vamos criar um novo projeto do tipo “Windows Forms Application“. Apesar de estarmos trabalhando com Windows Forms nesse artigo, você pode utilizar esses mesmos ensinamentos em projetos web (inclusive, caso você ainda não tenha visto, eu já mostrei no passado como utilizar o Report Viewer com o ASP.NET MVC).
Após ter criado o projeto, vamos adicionar um DataSet tipado, chamado “DataSetFuncionario“:
Dentro desse DataSet, adicione uma nova DataTable chamada “Funcionario“, com as seguintes colunas:
Configure a coluna “FuncionarioID” como auto-incremento / chave primária e altere o tipo de dados das colunas relacionadas a datas para o tipo “DateTime“.
Agora que já temos o nosso DataSet, vamos criar o nosso relatório. Para fins de exemplo, criaremos um simples relatório de listagem de funcionários utilizando o próprio Wizard do Report Viewer:
Na tela de configuração do DataSet, dê o nome de “DataSetFuncionario” para o DataSet que será criado e, na lista “Data Source“, escolha o “DataSetFuncionario” que criamos anteriormente:
Na próxima tela do Wizard, arraste todos os campos da tabela para dentro da área de valores:
Não esqueça de desabilitar o somatório da coluna “FuncionarioID“. Por padrão, qualquer coluna numérica que arrastarmos para a área de valores do Wizard ele configurará automaticamente um somatório:
Finalize o Wizard escolhendo um esquema de cores que te agradar mais. E o resultado do layout será parecido com este:
Uma vez criado o relatório, chegou a hora de exibi-lo. Para isso, vamos adicionar um controle do Report Viewer no nosso formulário e, na smart tag do controle, vamos selecionar o relatório que acabamos de criar:
Por fim, vamos até o code-behind, onde temos que preencher os dados do DataSet. Em uma aplicação do mundo real, esses dados provavelmente viriam do banco de dados. Porém, como estamos criando esse exemplo somente para demonstrar a filtragem de dados, vamos preencher esse DataSet “na mão“, utilizando este método:
private void PreencherDataSet()
{
var funcionario = DataSetFuncionario.Funcionario.NewFuncionarioRow();
funcionario.Nome = "André";
funcionario.Sobrenome = "Alves de Lima";
funcionario.DataNascimento = new DateTime(1984, 12, 31);
funcionario.Cargo = "Programador";
funcionario.DataAdmissao = new DateTime(2011, 11, 1);
DataSetFuncionario.Funcionario.AddFuncionarioRow(funcionario);
funcionario = DataSetFuncionario.Funcionario.NewFuncionarioRow();
funcionario.Nome = "Fulano";
funcionario.Sobrenome = "de Tal";
funcionario.DataNascimento = new DateTime(1978, 5, 24);
funcionario.Cargo = "Gerente de Projetos";
funcionario.DataAdmissao = new DateTime(2007, 9, 1);
DataSetFuncionario.Funcionario.AddFuncionarioRow(funcionario);
funcionario = DataSetFuncionario.Funcionario.NewFuncionarioRow();
funcionario.Nome = "Ciclano";
funcionario.Sobrenome = "Beltrano";
funcionario.DataNascimento = new DateTime(1989, 3, 17);
funcionario.Cargo = "Analista de Negócios";
funcionario.DataAdmissao = new DateTime(2007, 9, 1);
funcionario.DataDemissao = new DateTime(2012, 10, 19);
DataSetFuncionario.Funcionario.AddFuncionarioRow(funcionario);
funcionario = DataSetFuncionario.Funcionario.NewFuncionarioRow();
funcionario.Nome = "João";
funcionario.Sobrenome = "da Silva";
funcionario.DataNascimento = new DateTime(1969, 2, 8);
funcionario.Cargo = "Gerente Administrativo";
funcionario.DataAdmissao = new DateTime(2003, 5, 1);
funcionario.DataDemissao = new DateTime(2013, 1, 30);
DataSetFuncionario.Funcionario.AddFuncionarioRow(funcionario);
}
Note que criamos quatro funcionários: dois que ainda estão trabalhando na empresa (data de demissão em branco) e dois que já foram demitidos (com a data de demissão preenchida).
No Load do formulário, antes de chamarmos o método “RefreshReport“, vamos chamar esse método “PreencherDataSet“:
Agora que temos esse simples relatório onde exibimos a listagem de funcionários, como poderíamos fazer para exibirmos somente os funcionários que ainda não foram demitidos, ou seja, somente os funcionários ativos?
A primeira possibilidade é filtrarmos os dados diretamente no relatório. Para isso, vamos até as propriedades do grupo e, dentro da categoria “Filters” vamos adicionar um novo filtro:
Como queremos exibir somente os funcionários ativos, temos que filtrar pela data de demissão. Exibiremos somente os funcionários que tiverem a data de demissão vazia. A expressão para esse filtro ficaria a seguinte:
=IsNothing(Fields!DataDemissao.Value)
Em seguida, clique no segundo “fx” do filtro e adicione a expressão “=True“:
Como nós exibiremos somente os funcionários ativos, podemos alterar o título do relatório para “Funcionários ativos” e podemos também excluir a coluna “Data de Demissão“, uma vez que ela sempre estará vazia com o relatório filtrado:
Execute a aplicação e veja que o relatório será filtrado corretamente:
Porém, essa não é a melhor maneira de filtrarmos os dados do relatório. Ao filtrarmos os dados na tabela, caso tenhamos sumarizações ou contabilizações no nosso relatório, elas não levarão em consideração o filtro. Ou seja, nesse caso teríamos que alterar todas as funções de totalização para que o filtro seja considerado também. Além disso, estaríamos mandando uma infinidade de dados inúteis para o relatório (aumentando o seu tempo de processamento).
Uma melhor opção para filtrarmos os dados do relatório seria mandarmos os dados já filtrados. Dessa forma, não teríamos que alterar o relatório em nada. Vamos ver como podemos fazer isso de uma forma bem simples?
Opção 2 – Mandando um DataView para o relatório
Para filtrarmos uma DataTable em aplicações desenvolvidas com o .NET Framework, temos à nossa disposição a classe DataView. Essa classe serve, entre outras coisas, para filtrarmos dados de DataTables.
Levando em consideração o nosso exemplo, para filtrarmos os funcionários da DataTable de forma que retornemos somente os funcionários ainda não demitidos, poderíamos criar uma DataView com um RowFilter:
var dataView = new DataView(DataSetFuncionario.Funcionario);
dataView.RowFilter = "DataDemissao IS NULL";
Com a DataView em mãos, a única coisa que ficou faltando fazer é passa-la para o nosso relatório. Fazemos isso criando um novo ReportDataSource e adicionando-o na lista de DataSources do nosso relatório. Veja como fica o código do Load do formulário após essas alterações:
private void FormRelatorio_Load(object sender, EventArgs e)
{
PreencherDataSet();
var dataView = new DataView(DataSetFuncionario.Funcionario);
dataView.RowFilter = "DataDemissao IS NULL";
var dataSource = new Microsoft.Reporting.WinForms.ReportDataSource("DataSetFuncionario", dataView);
this.reportViewer.LocalReport.DataSources.Clear();
this.reportViewer.LocalReport.DataSources.Add(dataSource);
this.reportViewer.RefreshReport();
}
Pronto! Remova o filtro que criamos no relatório no passo anterior, execute a aplicação novamente e veja que o resultado é exatamente o mesmo:
Concluindo
A filtragem de dados é uma necessidade que quase sempre aparece quando estamos desenvolvendo os relatórios das nossas aplicações. Nesse artigo você aprendeu duas maneiras de filtrarmos dados com o Report Viewer: diretamente no relatório, ou filtrando os dados da DataTable com uma DataView.
Você viu também algumas desvantagens de filtrarmos os dados diretamente no relatório (como a questão das totalizações e o aumento no processamento de dados) e como a segunda alternativa é a mais recomendada.
Agora pergunto: você já teve que filtrar dados nos seus relatórios? Se sim, como é que você fez? Confesso que já utilizei muitas e muitas vezes a primeira opção. Porém, com o tempo, estou sempre tentando, na medida do possível, utilizar a segunda metodologia. Conte-nos nos comentários sobre as suas experiências com filtragens de dados no Report Viewer.
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, 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 logo abaixo.
Umas semanas atrás eu escrevi um artigo mostrando como alimentar relatórios do Report Viewer com o Entity Framework. Aí eu parei e pensei: será que dá para utilizar o Entity Framework com o Crystal Reports também? Depois de pesquisar um pouco e fazer uns testes, cheguei à conclusão que sim, é possível! Prepare-se para aprender com este artigo, como gerar relatórios do Crystal Reports com Entity Framework.
Criando o modelo de dados
A utilização das entidades do Entity Framework em relatórios criados com o Crystal Reports no Visual Studio é até mais tranquilo que o Report Viewer. Antigamente era preciso fazer uma gambiarra, convertendo as entidades em DataSet, porém, hoje em dia o Crystal Reports suporta as entidades do Entity Framework de forma nativa. Para demonstrar essa possibilidade, vamos utilizar um projeto do tipo “Windows Forms Application” e o mesmo modelo de dados do artigo sobre o Entity Framework com Report Viewer:
Não vou repetir todo o procedimento novamente neste artigo. Para saber como criar o modelo de dados que será utilizado neste artigo, confira a seção “Criando o modelo de dados” do artigo sobre Entity Framework com Report Viewer.
Desenhando o relatório
Uma vez criado o modelo de dados, vamos adicionar um novo relatório ao nosso projeto, dando o nome de “RelatorioPedido“. Os relatórios do Crystal Reports estão disponíveis dentro da categoria “Reporting“:
Quando o Crystal Reports perguntar o tipo de relatório que queremos criar, escolha a opção “relatório em branco“:
Assim que o relatório em branco tiver sido criado, vá até a aba “Field Explorer“, clique com o botão direito sobre “Database Fields” e escolha a opção “Database Expert“:
É justamente na janela “Database Expert” que vamos configurar a fonte de dados para o nosso relatório. No caso do Report Viewer, nós criamos uma classe que representava os dados do relatório. Nós poderíamos fazer o mesmo com o Crystal Reports, mas, isso não é obrigatoriamente necessário, uma vez que o Crystal Reports permite que nós adicionemos várias tabelas em um único relatório, desde que nós especifiquemos os relacionamentos entre elas.
Dito isso, encontre os objetos do nosso modelo (Cliente, ItemPedido, Pedido e Produto) dentro da seção “Project Data / .NET Objects” e adicione-os na parte de “Selected Tables“:
Em seguida, temos que ir até a aba “Links” para ajustarmos os relacionamentos entre as tabelas. Como o Crystal Reports cria os relacionamentos entre as tabelas ItemPedido e Pedido e entre as tabelas Pedido e Cliente com a direção errada, temos que recriá-los, conforme você pode conferir neste gif animado:
Agora que já temos a fonte de dados devidamente configurada, vamos criar um novo agrupamento no nosso relatório. Como você pode observar no diagrama do modelo, teremos informações dos pedidos na parte “mestre” do relatório e informações dos itens de pedido na parte “detalhe” do relatório. Portanto, temos que agrupar os dados pelo ID do Pedido. Para isso, clique com o botão direito sobre “Group Name Fields” e escolha a opção “Group Expert“:
Na janela de criação do grupo, escolha o campo “PedidoId” da tabela “Pedido“:
Antes de confirmar a criação do grupo, vá até a aba “Options” e configure a paginação do grupo (de forma que cada pedido seja impresso em uma página separada):
Agora é só desenhar o relatório com os dados do pedido no cabeçalho do agrupamento e os dados dos itens do pedido na área de detalhes. O resultado deve ficar parecido com a imagem abaixo:
Como nós não utilizamos o cabeçalho e o rodapé do relatório, nós podemos desabilita-los escolhendo a opção “Suppress” nas regiões “Report Header” e “Report Footer“:
Criando o formulário para a exibição do relatório
Com o relatório criado, agora só falta ajustarmos o formulário que fará a exibição do relatório. A primeira coisa que temos que fazer é adicionarmos o controle de exibição do Crystal Reports (disponível dentro da categoria “Reporting” da caixa de ferramentas). Feito isso, clique em “Choose a Crystal Report” e escolha o relatório que criamos anteriormente:
Não esqueça de alterar o nome do componente do relatório de “RelatorioPedido1” para “RelatorioPedido” (sem o “1” no final):
Vamos agora até o code-behind para passarmos os dados para o nosso relatório, o que é muito tranquilo de ser feito. Basta criarmos uma instância do nosso contexto do Entity Framework e chamarmos o método “SetDataSource” em cada uma das tabelas do nosso relatório, passando cada um dos DbSets do nosso contexto, só que convertidos para lista (método ToList):
public FormCrystalComEF()
{
InitializeComponent();
PopularBancoDeDadosSeNecessario();
using (var contexto = new Contexto())
{
RelatorioPedido.Database.Tables["CrystalReportsEF_Models_Cliente"].SetDataSource(contexto.Clientes.ToList());
RelatorioPedido.Database.Tables["CrystalReportsEF_Models_Produto"].SetDataSource(contexto.Produtos.ToList());
RelatorioPedido.Database.Tables["CrystalReportsEF_Models_Pedido"].SetDataSource(contexto.Pedidos.ToList());
RelatorioPedido.Database.Tables["CrystalReportsEF_Models_ItemPedido"].SetDataSource(contexto.ItensPedido.ToList());
}
}
Você encontra os nomes das tabelas na janela “Field Explorer” do relatório:
Muito tranquilo, não é mesmo? Vamos executar o projeto para ver se funciona? É claro que nunca funciona da primeira vez!
An unhandled exception of type ‘System.IO.FileNotFoundException’ occurred in mscorlib.dll Additional information: Could not load file or assembly ‘file:///C:\Program Files (x86)\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Common\SAP BusinessObjects Enterprise XI 4.0\win64_x64\dotnet1\crdb_adoplus.dll’ or one of its dependencies. The system cannot find the file specified.
Essa exceção não tem nada a ver com a utilização do Crystal Reports com o Entity Framework. Ela acontece também com outros tipos de fontes de dados. O problema é, na verdade, com o .NET Framework 4.5. Para conseguirmos utilizar essa versão do .NET Framework com o Crystal Reports, temos que adicionar um atributo na tag “startup” do nosso app.config:
Dessa forma, abra o arquivo app.config do projeto onde o controle do Crystal Reports está sendo utilizado e ajuste a tag startup desta maneira:
Pronto! Execute a aplicação novamente e veja o resultado, conforme esperado:
Concluindo
Alimentar relatórios do Crystal Reports com entidades do Entity Framework é muito tranquilo, até mais tranquilo do que com o Report Viewer, uma vez que não precisamos criar uma classe especial com os dados de todas as tabelas. Neste artigo você viu como criar um relatório mestre/detalhe no Crystal Reports baseado em quatro entidades do Entity Framework.
Você utiliza o Crystal Reports como ferramenta de relatórios nos seus projetos? Como você alimenta os seus relatórios? Com DataSets? Você sabia que dava para passar as entidades do Entity Framework diretamente para o seu relatório do Crystal Reports? Conte-nos a sua experiência nos comentários.
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se utilizando o formulário logo abaixo.