23 03 2016
Mandando uma DataTable filtrada para o Report Viewer
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“:
private void FormRelatorio_Load(object sender, EventArgs e) { PreencherDataSet(); this.reportViewer.RefreshReport(); }
Execute a aplicação e veja o resultado:
Opção 1 – Filtrando os dados no relatório
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.
Até a próxima!
André Lima
Image by Juhan Sonin used under Creative Commons
https://www.flickr.com/photos/juhansonin/5135576565
Lendo coordenadas GPS no Windows Forms Gerando relatórios do Crystal Reports com Entity Framework
Excelente artigo André.
Recentemente usei uma solução parecida, onde na verdade, o DataTable já filtrava os dados que eram obtidos do Banco. Entretanto, essa solução usando o DataView é certamente muito recomendada uma vez que essa “Filtragem” ocorre em memória.
Show de bola!
Olá Paulo, obrigado pelo comentário!
Pois é.. Se não for possível já trazer os dados filtrados do banco para dentro da DataTable (o que é mais recomendado, evitando tráfego desnecessário de dados), a saída com a DataView é uma ótima opção..
Abraço!
André Lima
Ola André.
Eu tenho um relatoroi com duas colunas que elas devem ser preenchidas com um método de uma classe externa do meu projeto. Voce tem algum artigo que explique ou de uma nocao disso? Se puder me add no skype r.cardoso1
Obrigado
Olá Rafael!
Qual é o retorno desse método externo ao seu projeto? Se ele for uma DataTable ou lista de objetos, você pode passar diretamente para o Report Viewer sem problema algum.. Agora, se for algum outro tipo de retorno mais complexo, talvez você tenha que criar um DataSet tipado ou classe específica para alimentar o seu relatório.. Aí você converte os valores retornados por esse método nesse DataSet ou classe que você criou..
Se você puder passar mais detalhes do retorno dessa classe e da estrutura do seu relatório, talvez eu consiga te ajudar melhor..
Abraço!
André Lima