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/

2 thoughts on “Trabalhando com o Report Viewer no MVC

Deixe uma resposta

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