André Alves de Lima

Talking about Software Development and more…

Definindo a estrutura de campos do Report Viewer sem DataSet ou classe

DataSets são elementos importantíssimos no desenvolvimento de relatórios, independente da ferramenta que você estiver utilizando. Afinal de contas, são eles que definem a estrutura de campos que os relatórios poderão exibir. Com o Report Viewer não poderia ser diferente – ele também utiliza o conceito de DataSets na definição dos relatórios.

Um grande desafio que temos quando a nossa aplicação vai crescendo é: como conseguimos manter a imensa quantidade de DataSets ou classes de dados no nosso projeto sem que tudo acabe ficando uma verdadeira bagunça? Não seria muito legal se pudéssemos criar a estrutura de campos do Report Viewer sem DataSet ou classe? No Crystal Reports temos os arquivos TTX para nos ajudar com essa tarefa, mas e quanto ao Report Viewer? Ele tem algo parecido?

Por padrão, no Report Viewer nós só conseguimos definir a estrutura de campos dos relatórios com DataSets tipados ou classes de dados. Para resolver essa limitação, eu estive trabalhando algumas horas em uma pequena ferramenta que vai nos ajudar a editar a definição de campos dos relatórios do Report Viewer sem que nós precisemos criar DataSets tipados ou classes de dados no nosso projeto.

No artigo de hoje eu vou mostrar para você como é que essa ferramenta funciona. Gostou da ideia? Então vamos lá!

Como são definidos os DataSets do Report Viewer?

Antes de prosseguirmos, é importante entendermos a maneira como o Report Viewer salva a definição dos DataSets no arquivo RDLC. Não sei se você sabe, mas os arquivos RDLC são basicamente XMLs com uma estrutura pré-estabelecida. Ao abrirmos um arquivo RDLC em um editor de texto, teremos uma ideia de como esses arquivos são estruturados:

Como já era de se imaginar, as estruturas dos DataSets do relatório também estão definidas nesse XML, mais especificamente dentro das tags “DataSources” e “DataSets“. Veja só um exemplo dessa definição:

Independentemente da maneira que nós utilizemos para adicionar um DataSet no Report Viewer (seja através de DataSets tipados, classes de dados ou serviços), o arquivo RDLC conterá essas duas tags onde a estrutura de campos será definida.

Com isso em mente, se nós quisermos adicionar, remover ou alterar campos de um DataSet do nosso relatório (ou até mesmo criar um novo DataSet), nós só temos que alterar o conteúdo dessas tags.

Apresentando o Report Viewer DataSet Editor

Se eu chegasse aqui e falasse que nós teremos que editar essas tags manualmente no arquivo RDLC, você provavelmente riria da minha cara. As chances são grandes de quebrarmos alguma coisa na estrutura do arquivo se nós começarmos a editar o seu conteúdo manualmente.

Pensando nisso, eu investi algumas horas na criação de uma ferramenta que faz a leitura do arquivo RDLC, mostra a estrutura dos DataSets do relatório em um grid e permite adicionar, alterar ou remover campos, bem como criar novos DataSets para o relatório:

Essa ferramenta, a qual eu dei o singelo nome de “Report Viewer DataSet Editor“, é open source e o código está disponível no GitHub. Como você pode notar, o projeto está bem no começo. Ainda não adicionei descrição do projeto, comentários, etc. E não repare no código! Ele está bem rudimentar, sem testes de unidade e com uma estrutura bem simples por enquanto.

Eu testei a ferramenta com alguns relatórios de exemplo e ela funcionou muito bem. Mas, ainda tem muito para testar e evoluir. Caso você queira contribuir, é só mandar um pull request lá no GitHub.

Para utilizar essa ferramenta, baixe a última release neste link. Como o executável não está assinado, se você tentar rodar a aplicação em um computador com Windows 10, o SmartScreen vai falar que a aplicação não é confiável. Obviamente, essa aplicação é confiável (você pode ver o código fonte no próprio GitHub), portanto ignore essas mensagens do SmartScreen:

Se por algum motivo você não confiar no executável, baixe o código fonte no GitHub e compile você mesmo utilizando o Visual Studio.

Criando um novo relatório

Para demonstrar o funcionamento dessa ferramenta, vamos criar um novo projeto do tipo “Windows Forms Application” e, dentro desse projeto, vamos adicionar um novo relatório, dando o nome de “Relatorio.rdlc” para o arquivo que será criado:

Nota: caso você não encontre o item “Report”, veja como adicionar o Report Viewer no Visual Studio 2015 e no Visual Studio 2017.

Com o arquivo RDLC criado, vamos abri-lo com o Report Viewer DataSet Editor para definirmos um novo DataSet e a sua estrutura de campos. Como o relatório ainda não tem nenhum DataSet definido, o ComboBox “DataSet” e o grid aparecerão em branco. A única opção nesse caso é clicarmos no botão “New DataSet” para criarmos um novo DataSet nesse relatório. Vamos dar o nome de “DataSetRelatorio” para o novo DataSet que será criado:

Em seguida, vamos adicionar as colunas desse DataSet. Para simplificarmos, vamos criar somente duas colunas do tipo “String” – uma chamada “Nome” e outra chamada “Sobrenome“:

Salve as alterações clicando no botão “Save changes“:

Pronto! A partir daí, se voltarmos no designer do nosso relatório, nós veremos que o “DataSetRelatorio” estará disponível:

Vamos montar um layout bem simples para o relatório, adicionando somente uma tabela com as duas colunas (nome e sobrenome):

Alimentando o relatório através de um DataGridView

Agora que nós temos o nosso relatório montado, vamos ver como podemos alimentá-lo sem utilizar nenhuma classe ou DataSet. Imagine que nós tenhamos uma janela com um DataGridView, contendo as colunas “Nome” e “Sobrenome“:

Como o método de carregamento de dados do Report Viewer suporta não só DataTables, mas também, coleções enumeráveis, nada impede que nós criemos um Enumerable dos dados do DataGridView para mandarmos para o relatório. Isso é muito fácil de ser implementado, utilizando LINQ.

Para não perdermos tempo montando um formulário para exibirmos os dados desse relatório, vamos utilizar o formulário que eu criei no meu artigo onde eu mostro como carregar múltiplos relatórios do Report Viewer com o mesmo formulário. Uma vez adicionado esse formulário no projeto, veja como fica fácil o código para montarmos uma coleção enumerável utilizando os dados do DataGridView:

        // C#
        private void btRelatorio_Click(object sender, EventArgs e)
        {
            var dadosRelatorio = from DataGridViewRow linha in dataGridViewNomes.Rows
                                 select new
                                 {
                                     Nome = linha.Cells["Nome"].Value,
                                     Sobrenome = linha.Cells["Sobrenome"].Value
                                 };

            FormRelatorio.ShowReport("ReportViewerSemDataSet.Relatorio.rdlc", true, new Dictionary<string, object>() { { "DataSetRelatorio", dadosRelatorio.AsEnumerable() } });
        }
    ' VB.NET
    Private Sub btRelatorio_Click(sender As Object, e As EventArgs) Handles btRelatorio.Click
        Dim DadosRelatorio = From linha In dataGridViewNomes.Rows
                             Select New With {.Nome = linha.Cells("Nome").Value, .Sobrenome = linha.Cells("Sobrenome").Value}

        FormRelatorio.ShowReport("ReportViewerSemDataSet.VB.Relatorio.rdlc", True, New Dictionary(Of String, Object)() From {{"DataSetRelatorio", DadosRelatorio.AsEnumerable()}})
    End Sub

Execute a aplicação, preencha o grid com alguns dados, chame o relatório e veja o resultado:

Ahá! Aí está um relatório do Report Viewer que foi construído e carregado sem a utilização de DataSets tipados ou classes de dados. Bacana, não é mesmo?

Concluindo

O fato de sempre termos que definir DataSets tipados ou classes de dados para desenharmos o layout dos nossos relatórios do Report Viewer pode fazer com que o nosso projeto se torne bem bagunçado. Imagine um projeto com 50 relatórios. Muito provavelmente esse projeto terá inúmeros DataSets tipados ou classes de dados que só servem para desenharmos e carregarmos os relatórios.

No artigo de hoje eu apresentei para você a ferramenta open-source que eu estou desenvolvendo, o Report Viewer DataSet Editor. Essa ferramenta permite a definição e alteração dos DataSets do relatório sem que precisemos criar DataSets tipados ou classes de dados no nosso projeto. Isso facilita bastante a nossa vida e deixa o nosso projeto mais limpo.

Atenção! Essa ferramenta ainda está bem crua! Eu testei com alguns relatórios e funcionou perfeitamente, porém, você deve utilizá-la por sua conta e risco. Lembre-se sempre de fazer um backup do seu arquivo RDLC antes de aplicar as alterações!

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.

Até a próxima!

André Lima

Image by Pixabay used under Creative Commons
https://pixabay.com/en/document-agreement-documents-sign-428338/

Newsletter do André Lima

* indicates required



Powered by MailChimp

23 thoughts on “Definindo a estrutura de campos do Report Viewer sem DataSet ou classe

  • João Victor disse:

    Muito bom André, parabéns pela ferramenta.
    Em minha opinião, para projetos simples é muito bacana (inclusive o visual studio poderia ter uma aplicação dessa dentro)

    Porém pra mim que utilizo Entity com CodeFIrst, a criação de uma classe e posteriormente uma lista de objetos dessa classe faz mais sentido e torna o processo de alimentação de dados mais fácil!

    Grande abraço

    • andrealveslima disse:

      Olá João, muito obrigado pelo comentário!

      Realmente com o Code First normalmente você não vai precisar disso, uma vez que as classes já estão no projeto e você já pode utilizá-las diretamente no designer do relatório..

      Porém, se você for fazer algum relatório que envolva mais de uma tabela e não quiser utilizar a funcionalidade de Lookup no relatório (ou seja, se você quiser juntar todas as informações do relatório em um DataSet só), você pode utilizar a ferramenta para evitar ter que criar uma classe específica só para representar as informações do relatório.. Aí na hora de exibir você só faz a query LINQ juntando tudo em um tipo anônimo e manda para o relatório como um IEnumerable..

      Abraço!
      André Lima

  • deuzivaldo disse:

    Professor adorei mesmo.
    Tenho uma pergunta. Eu criei um gerador de currículo que ele pode ter, dependendo da pessoa uma ou duas ou três paginas. Só que quando vou imprimir se for uma pagina ele imprimi as duas paginas em branco não sei onde estou errando gostaria de ter esta sua ajuda.
    obrigado e boa noite

    • andrealveslima disse:

      Olá Deuzivaldo, obrigado pelo comentário!

      Estranho hein.. Esse tipo de comportamento costuma acontecer quando você define uma área para o relatório que passe dos limites do tamanho da página.. Aí ele gera páginas adicionais em branco..

      Mas, só consigo analisar melhor se você der mais detalhes.. Mande uns screenshots mostrando como ficou o relatório.. Ou, se possível, mande o arquivo RDLC para eu dar uma olhada..

      Abraço!
      André Lima

      • deuzivaldo disse:

        Bom dia professor mais foi justamente isso que eu fiz eu desenhei as três pagina cera isso.
        Obrigado

        • andrealveslima disse:

          Olá Deuzivaldo!

          Não consegui entender direito como você desenhou o relatório.. Será que você poderia mandar uns screenshots ou uma cópia do arquivo RDLC para eu dar uma olhada?

          Abraço!
          André Lima

  • Vicente disse:

    Ótimo trabalho, parabéns pela iniciativa.

  • Marcos Gerene disse:

    André, em primeiro ligar parabéns pelo conteúdo!

    Hoje uso FastReports (ferramenta paga) para relatórios, o meu principal motivo para escolha foi que eu uso Lista de objetos para impressao, ex: List e não consegui entender e nem achar suporte para um problema simples como mostrar a o nome da cidade.

    Ex: Cliente.Cidade.Nome aonde cidade é outro objeto.

    Eu na época até fiz um post sobre isso no MSDN falamos algo sobre “Nested object” mas nao consegui solução e preferi comprar uma ferramenta de maior qualidade.

    Abraços

    • andrealveslima disse:

      Olá Marcos!

      Pois é.. Tem alguns pontos que são complicados com o Report Viewer mesmo.. Esse negócio de nested objects eu não saberia como resolver, a não ser criando uma classe específica que faça um “merge” das propriedades para alimentar o relatório.. O Crystal Reports é um pouco mais flexível nesse tipo de situação, mas o deployment dele é uma desgraça.. É sempre assim.. Vantagens e desvantagens, tem que pesar para ver o que é melhor para cada projeto..

      O FastReport eu nunca utilizei.. Dando uma olhada aqui no site deles, pareceu um pouco com a ferramenta de relatórios da DevExpress..

      Abraço!
      André Lima

      • Marcos Gerene disse:

        O Fast Reports pra mim foi a melhor ferramenta que ja usei pra report, testei ambos trial (Fast e DevExpress) e pra mim o Fast se saiu melhor.

        O Nested realmente eu so consegui com uma terceira classe, mas acho que perderia toda a magica de utilizar as classes ja escritas…

        Abracos

        • andrealveslima disse:

          Legal, Marcos.. Valeu pelo retorno.. :)

          Anotei aqui para dar uma avaliada no Fast Reports assim que possível..

          Abraço!
          André Lima

  • […] problema nessa etapa e o campo “Via” não esteja aparecendo, você pode utilizar o Report Viewer DataSet Editor para adicionar esse campo no seu relatório […]

  • Rodolfo Reyes disse:

    Muchas gracias por esta herramienta.

  • Aleff disse:

    Olá, ainda sou iniciante com .Net, estou criando um relatório no ReportViewer para um projeto Web, porém como normalmente faço com TableAdapter não está dando certo, pois o Comando SQL que preciso utilizar não é suportado o comando “PIVOT”.
    Como não consegui utilizar tableAdapter criei uma Lista do objeto criado a partir da Query, porém não faço ideia como passar essa lista para meu Relatório.
    Vi alguns posts seus, porém ainda não consegui entender como passar para relatórios de projetos Web.
    Desde de já agradeço.

    • andrealveslima disse:

      Olá Aleff!

      É justamente isso que eu mostro nesse artigo.. Você chegou a ler por completo? Na seção “Alimentando o relatório através de um DataGridView” eu mostro como alimentar o relatório com uma lista que foi criada com base nos dados registrados no DataGridView..

      Abraço!
      André Lima

      • Aleff disse:

        Olá, agradeço por ter respondido. Consegui resolver o problema, mas de outra forma, pois como meu projeto é web não consegui passar os dados da mesma forma que mostra no exemplo.

        • andrealveslima disse:

          Olá Aleff!

          Beleza, sem problema.. O importante é que você conseguiu resolver o problema.. Qualquer coisa estamos aí..

          Abraço!
          André Lima

  • Kenio Roberto disse:

    Ola estou iniciando em .Net e estou desenvolvendo um sistema e preciso assim que fechar uma venda imprimir um recibo com Report view com a lista de produtos comprados e filtrados pelo ID da compra com dados como nome do cliente data hora da compra . gostaria de saber se vc possui um tutorial que pode me ajudar .

    • andrealveslima disse:

      Olá Kenio!

      Eu não tenho um exemplo exatamente desse jeito que você descreveu (obviamente), mas tem uma infinidade de artigos onde eu mostro como gerar relatórios no Report Viewer (como esse mesmo onde você adicionou o comentário).. Dê uma olhada nos artigos dentro da categoria Report Viewer que provavelmente você conseguirá ter uma boa ideia de como resolver esse seu problema específico..

      Abraço!
      André Lima

  • Romario dos Santos disse:

    Olá André tudo bem cara.Parabéns pelo seu trabalho.Estava assistindo suas vídeo aulas para tentar resolver um problema mas ainda não conseguir.
    Aparentemente e fácil mas sou meio leigo no assunto.

    Tenho a seguinte aplicação, vou ser bem claro.

    Tenho um banco de dados MySql onde faço vários consultas de relatório e preencho um dataGreedView, praticamente 5 relatórios com diferente SQL de consultas.
    Todas funcionam corretamente, todas preenche o dataGreeView.

    Ai passo os dados do dataGreedView para um DataTable para popular o DataSet e preencher o relatorio reportView.

    DataTable newDt = new DataTable();
    newDt = (DataTable)dataGridView1.DataSource;
    this.reportViewer1.LocalReport.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource(“DataSet_RelatorioGeral”, newDt));
    this.reportViewer1.RefreshReport();

    Nota.Esse comando funciona muito bem e preenche o relatório certinho.
    Porém eu tenho que definir o DataSet no formulario no ReporView1, assim teria que fazer 5 ReportView 1,2,3,4,5;
    Eu faço as consultas e exibo o relatorio no mesmo formulário.

    Resumindo:
    Tenho um DataSet –DataSet_RelatorioGeral
    dentro do DataSet tenho 5 DataTable — Tabela1,Tabela2,Tabela3,Tabela 4, 5
    Ai tenho 5 report.rdlc apontado pro DataSet_RelatorioGeral, e para as tabela respectiva.

    Ai gostaria de chamar o ReportView1,que esta no mesmo fomulário
    passando como parameter o (“DataSet_RelatorioGeral”,”Tabela1″,newDt)

    newDt e o DataTable gerado a partir do dataGreedView;

    Como posso fazer Isso?

Deixe uma resposta

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