André Alves de Lima

Talking about Software Development and more…

Trabalhando com o Report Viewer no MVC

O Report Viewer só tem suporte nativo ao Windows Forms e Web Forms, ou seja, quem trabalha com outras plataformas (como WPF ou MVC) precisa fazer alguns ajustes para poder utilizá-lo nos seus projetos. No caso do WPF, é só adicionar um WindowsFormsHost com o controle do Report Viewer dentro, como eu mostrei neste artigo. Já no ASP.NET MVC, ou adicionamos um WebForm (não recomendado) ou utilizamos uma biblioteca que possibilita a criação dos relatórios no “estilo MVC” de desenvolver.

Eu já escrevi um artigo mostrando como utilizar o Report Viewer no ASP.NET MVC, onde eu mostro as duas modalidades mencionadas acima. Devido à popularidade desse artigo, eu resolvi fazer uma versão em vídeo explorando um pouco mais a ideia da utilização da biblioteca “ReportViewerForMVC“. No vídeo eu mostro também um problema que temos ao utilizá-la com sub-relatórios. Confere aí:

Biblioteca ReportViewerForMVC

A biblioteca que podemos utilizar para trabalharmos com o Report Viewer no MVC (sem termos que adicionar um WebForm manualmente) é a “ReportViewerForMVC“. Como você pode reparar no site da biblioteca no CodePlex, ela não tem sido atualizada há um bom tempo. Porém, ela funciona bem com a versão 11 do Report Viewer, então ela acaba dando conta do recado.

Para adicionarmos uma referência a essa biblioteca no nosso projeto, nós utilizamos o NuGet. Basta procurarmos por “ReportViewerForMVC” na janela de gerenciamento de pacotes do NuGet ou digitarmos “Install-Package ReportViewerForMVC” no Package Manager Console:

Essa biblioteca adicionará a referência às dlls “Microsoft.ReportViewer.WebForms” e “ReportViewerForMvc“.

Outras referências necessárias

Em computadores que tenham a runtime do Report Viewer instalada, somente a referência à biblioteca já é o suficiente para que o nosso projeto funcione. Porém, para o caso em que a versão correta da runtime do Report Viewer não estiver instalada, algumas outras referências serão necessárias.

Por segurança, eu recomendo que você adicione pelo NuGet as referências às bibliotecas “MicrosoftReportViewerWebForms” e “Microsoft.SqlServer.Types” (versão 11.0.0):

O código do Controller

Uma vez adicionadas todas as referências necessárias, vamos partir para a criação do código do nosso Controller. Adicione um novo Controller vazio do MVC na pasta “Controllers” para que possamos fazer essa implementação.

A ideia é muito simples. Primeiramente nós temos que carregar os dados da nossa fonte de dados e armazená-los em um DataSet ou coleção. Em seguida, nós criamos uma instância da classe “Microsoft.Reporting.WebForms.ReportViewer” e configuramos algumas propriedades (como caminho do relatório e sua fonte de dados). Por fim, nós retornamos essa instância de “ReportViewer” através da ViewBag.

Veja só este exemplo:

        // C#
        public ActionResult Index()
        {
            var ds = ObterDados();

            var viewer = new Microsoft.Reporting.WebForms.ReportViewer();
            viewer.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local;
            viewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + @"CaminhoDoSeuRelatorio.rdlc";
            viewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource("NomeDoDataSetNoRelatorio", (System.Data.DataTable)ds.NomeDaDataTable));

            ViewBag.ReportViewer = viewer;

            return View();
        }
            ' VB.NET
            Dim Ds = ObterDados()

            Dim Viewer = New Microsoft.Reporting.WebForms.ReportViewer()
            Viewer.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local
            Viewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + "CaminhoDoSeuRelatorio.rdlc"
            Viewer.LocalReport.DataSources.Add(New Microsoft.Reporting.WebForms.ReportDataSource("NomeDoDataSetNoRelatorio", DirectCast(Ds.NomeDaDataTable, System.Data.DataTable)))

            ViewBag.ReportViewer = Viewer

            Return View()

E como fica a View?

Com o Controller em mãos, podemos partir para a criação da nossa View. O código dela é muito simples. Primeiramente nós temos que adicionar uma referência à biblioteca “ReportViewerForMvc” (com uma cláusula “using” no início da View). Adicionada essa referência, nós teremos alguns métodos extras à nossa disposição, como o “Html.ReportViewer“. Ao chamarmos esse método passando a instância do ReportViewer da ViewBag, o relatório será renderizado naquele local:

<!-- C# -->
@using ReportViewerForMvc;

@{
    ViewBag.Title = "Index";
}

<h2>Relatório</h2>

@Html.ReportViewer(ViewBag.ReportViewer as Microsoft.Reporting.WebForms.ReportViewer)
<!-- VB.NET -->
@Imports ReportViewerForMvc

@Code
    ViewData("Title") = "Index"
End Code

<h2>Index</h2>

@Html.ReportViewer(DirectCast(ViewBag.ReportViewer, Microsoft.Reporting.WebForms.ReportViewer))

Execute o projeto e veja o resultado:

Ajuste automático do tamanho do controle

Se observarmos o resultado apresentado na imagem acima, veremos que o controle está com um tamanho fixo (o tamanho padrão). No controle do Report Viewer web, temos a possibilidade de configurarmos um tamanho dinâmico, ou seja, o controle será automaticamente redimensionado dependendo do tamanho do relatório.

Para atingirmos esse resultado, temos que alterar a propriedade “SizeToReportContent” para verdadeiro, além de configurarmos o Width e Height de forma que eles ocupem 100% da área disponível na View. Coloque este código antes de retornar o controle para a ViewBag e veja a diferença:

            // C#
            viewer.SizeToReportContent = true;
            viewer.Width = System.Web.UI.WebControls.Unit.Percentage(100);
            viewer.Height = System.Web.UI.WebControls.Unit.Percentage(100);
            ' VB.NET
            Viewer.SizeToReportContent = True
            Viewer.Width = System.Web.UI.WebControls.Unit.Percentage(100)
            Viewer.Height = System.Web.UI.WebControls.Unit.Percentage(100)

Problemas com sub-relatórios

A biblioteca Report Viewer for MVC funciona muito bem até o momento em que tivermos que trabalhar com sub-relatórios. Para exibirmos um relatório que tenha sub-relatório, o código do Controller ficaria assim:

        // C#
        DataSet ds;

        public ActionResult Index()
        {
            ds = ObterDados();

            var viewer = new Microsoft.Reporting.WebForms.ReportViewer();
            viewer.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local;
            viewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + @"CaminhoDoSeuRelatorio.rdlc";
            viewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource("NomeDoDataSetNoRelatorio", (System.Data.DataTable)ds.NomeDaDataTable));
            viewer.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;

            ViewBag.ReportViewer = viewer;

            return View();
        }

        private void LocalReport_SubreportProcessing(object sender, Microsoft.Reporting.WebForms.SubreportProcessingEventArgs e)
        {
            e.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource("NomeDoDataSetNoSubRelatorio", (System.Data.DataTable)ds.NomeDaDataTableDetalhe));
        }
        ' VB.NET
        Private Ds As DataSet

        Public Function Index() As ActionResult
            Ds = ObterDados()

            Dim Viewer = New Microsoft.Reporting.WebForms.ReportViewer()
            Viewer.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local
            Viewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + "CaminhoDoSeuRelatorio.rdlc"
            Viewer.LocalReport.DataSources.Add(New Microsoft.Reporting.WebForms.ReportDataSource("NomeDoDataSetNoRelatorio", DirectCast(Ds.NomeDaDataTable, System.Data.DataTable)))
            AddHandler Viewer.LocalReport.SubreportProcessing, AddressOf LocalReport_SubreportProcessing

            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()
        End Function

        Private Sub LocalReport_SubreportProcessing(sender As Object, e As Microsoft.Reporting.WebForms.SubreportProcessingEventArgs)
            e.DataSources.Add(New Microsoft.Reporting.WebForms.ReportDataSource("NomeDoDataSetNoSubRelatorio", DirectCast(Ds.NomeDaDataTableDetalhe, System.Data.DataTable)))
        End Sub

Porém, se testarmos esse cenário com um breakpoint no evento “SubReportProcessing“, veremos que ele não será disparado. Isso acontece porque a biblioteca tem um bug que evita o disparo desse evento. Existe até uma “issue” criada no site da biblioteca falando sobre esse problema.

No link acima você pode notar que eu respondi aquela “issue” com a correção necessária para que os sub-relatórios funcionem. Eu fiz essa correção, recompilei a biblioteca e você pode baixar a versão corrigida aqui. Substitua essa dll na pasta “packages” e na pasta “Bin” do seu projeto para que os sub-relatórios funcionem corretamente. E acompanhe este repositório no meu GitHub, onde eu pretendo dar manutenção nessa biblioteca e disponibilizar atualizações.

Baixe o projeto de exemplo

Para baixar o projeto de exemplo desse artigo, assine a minha newsletter. Ao fazer isso, além de ter acesso ao projeto, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário no final do artigo.

Até a próxima!

André Lima

Photo by Peter Shanks used under Creative Commons
https://pixabay.com/en/startup-start-up-notebooks-creative-593327/

Song Rocket Power Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

39 thoughts on “Trabalhando com o Report Viewer no MVC

  • Andre ! tenho um projeto ASP.NET MVC 5 no Visual Studio 2017, estou utilizando no meu MODEL, as tabelas via LINQ TO SQL. To tendo dificuldades tem passar os dados pro REPORT VIEWER.

    Voce pode me ajudar ! Desde já lhe agradeço !

    • andrealveslima disse:

      Olá Claudionor!

      O seu relatório já está pronto (e você só está tendo dificuldades em passar os dados) ou você está tendo problemas já na criação do relatório? Me explica melhor em qual etapa você está tendo dificuldades para que eu consiga te ajudar melhor..

      De qualquer forma, eu sugiro que você dê uma olhada neste outro artigo, talvez ele te ajude:

      Gerando relatórios do Report Viewer com Entity Framework

      Abraço!
      André Lima

  • Excelente tutorial, achei muito legal a sua disposição em dar continuidade a biblioteca.

    • Depois de muitas tentativas e erros funcionou no Visual Studio 2017 ASP.MVC5, mas tive que usar a versão do Microsoft.SqlServer.Types 14.0.314.76, não sei porque não funcionava na versão 11.0.0.

      • andrealveslima disse:

        Olá mais uma vez Pablo!

        Que estranho hein.. Era para ter funcionado com a outra versão do SqlTypes.. De qualquer maneira, obrigado por ter voltado aqui e postado a solução para o seu problema.. :)

        Abraço!
        André Lima

    • andrealveslima disse:

      Muito obrigado, Pablo! Fico feliz que você tenha gostado..

      Abraço!
      André Lima

  • Você pretende disponibilizar as atualizações do biblioteca via NuGet?

    • andrealveslima disse:

      Olá novamente, Pablo! Assim que eu conseguir um tempo para fazer isso, sim.. O problema é que atualmente o tempo está bem escasso, hehehe.. Então, por enquanto o jeito é copiar a dll compilada e adicionar a referência na mão mesmo..

      Abraço!
      André Lima

  • Douglas Fernandes disse:

    Gostaria de saber se tem algum jeito de gerar o relatório diretamente em PDF sem passar pela View do relatório!! se tiver como, coloca junto ao cometário

  • jibin disse:

    Is there any way to show a sort of please wiat message till the the time the report was loaded completly in UI

    • andrealveslima disse:

      Hi!

      Usually the Report Viewer control already displays a message “Loading data” while it is drawing.. Isn’t it working in your case?

      André Lima

  • Valentin Gosetto disse:

    Hi,
    I followed your tutorial and it works great. Then if I want to add a new report, it shows the report with no records. For instance, in the controller, when I fill de dataset I use Take(5).ToList() then in the report it shows the columns header and 5 blank records. I tried adding the new datatable to the existing DataSet and adding a new DataSet and both have the same behaviour. Can you imagine what is going on? Thanks in advance.

    • andrealveslima disse:

      Hi Valentin!

      This usually happens when the names of the properties (or columns) in the data source don’t match exactly the names of the columns in the report DataSet.. They have to match 100% exactly (they are case sensitive).. Did you check that already?

      Regards,
      André Lima

  • ricardo disse:

    assinei como faco pra baixar o fonte?

    • andrealveslima disse:

      Olá Ricardo!

      Você deve ter recebido o link no e-mail de confirmação da inscrição.. Mas, de qualquer forma, eu acabei de mandar novamente o link no seu e-mail..

      Abraço!
      André Lima

  • Eurico Filipe disse:

    Olá, adorei o seu post nem sabes o quanto ele foi util.

    Estou com um probleminha, consegui gerar o relatorio mais a barra de ferramentas do relatorio aparece branca e sem os botões de gerar e imprimir o report.

    • andrealveslima disse:

      Olá Eurico!

      Você tem certeza que está utilizando a última versão da biblioteca? Isso acontecia em algumas versões atrás.. Você poderia enviar um screenshot mostrando essa situação?

      Abraço!
      André Lima

  • Boa noite Andre, eu baixei a dll da última release no seu github, funcionou perfeitamente para relatórios que não tem subrelatórios, mas eu não foi porque não aciona o evento LocalReport_SubreportProcessing, eu segui o seu exemplo.

    • Depois de fazer um monte de testes eu consegui, até clonei uma cópia do seu projeto no github e atualizei ela para as versões mais recentes das bibliotecas dependentes para ver se era esse o problema já que eu estou usando o Visual Studio 2017. Mas no final das contas o problema era que eu estava passando parâmetros para o subrelatório no relatório pai que não estava cadastrados no subrelatório.

      Agora está funcionando perfeitamente.

      • andrealveslima disse:

        Olá Pablo!

        Que bom que você conseguiu encontrar o problema na passagem do parâmetro entre os relatórios.. Qualquer outra dificuldade, é só entrar em contato..

        Abraço!
        André Lima

  • Freddy Alday Sfarcic disse:

    Felicitaciones. Muy buen tutorial.
    me ha servido mucho. Gracias por el aporte. Saludos

  • Cláudio de Miranda Cirne disse:

    Muito bom!!

    • andrealveslima disse:

      Olá, Cláudio, muito obrigado pelo comentário!

      Que bom que você gostou.. :)

      Qualquer dificuldade, é só falar..

      Abraço!
      André Lima

  • denilson Carlos disse:

    Boa Tarde

    o servidor de hospedagem tem que ter suporte para o reportView ?

  • Dyego Scofield Furletti disse:

    Olá André mesmo após a aplicar o comando SizeToReportContent = true; não consegui colocar o visualizador do relatório como full. Segue meu código.

    DataTable dtDados = CertificadosBLL.RelatorioCerificadoSituacao(“”);

    var viewer = new Microsoft.Reporting.WebForms.ReportViewer();
    viewer.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local;
    viewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + @”Reports\Certificados\Report1.rdlc”;
    viewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource(“DataSetStatus”, dtDados));

    viewer.SizeToReportContent = true;
    viewer.Width = System.Web.UI.WebControls.Unit.Percentage(100);
    viewer.Height = System.Web.UI.WebControls.Unit.Percentage(100);

    viewer.LocalReport.Refresh();

    • andrealveslima disse:

      Olá Dyego!

      Cara, estranho hein.. Não sei o que pode estar acontecendo.. Nunca tive esse problema.. Você já tentou criar um projeto novo do zero para ver se o problema acontece também em um projeto zerado? Caso funcione em um projeto zerado, pode ser alguma coisa no seu layout, CSS ou coisa do tipo..

      Abraço!
      André Lima

  • Alvim Praia disse:

    Olá André

    Tou com um projecto web com BD, e preciso que o sistema gere relatórios filtrados pela visão por exemplo trazer para o relatório registos com a mesma data.

    Vi e tentei implementar uma dataView com o RowFilter como fizeste noutro exemplo com Windows Forms, mas o relatório não vem filtrado.

    Podes me ajudar com isso?

    • andrealveslima disse:

      Olá Alvim!

      Vou repetir aqui o que eu te respondi no seu comentário lá no Youtube:

      Era para ter funcionado com o DataView.. Como é que ficou o código? Você tentou debugar para ver se o conteúdo da DataView está sendo corretamente filtrado e se os dados estão sendo passados certinho para o relatório?
      Outra opção seria tentar fazer o filtro através de parâmetro no relatório.. Você mandaria o valor do parâmetro para o relatório e filtraria o conteúdo com base nele..

      Abraço!
      André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *