Binding the ReportViewer Control to Multiple Generic Collections
page 1 of 1
Published: 06 Feb 2006
Unedited - Community Contributed
Abstract
It is easy to get the ReportViewer Control to report off of a DataSet that is set during design time. But what if you want to use code to cause one ReportViewer control to display many totally different reports using your own O/R mapper collection instead? It is not easy to see how to do this, but it is only a few lines of code once perceived. Now, using generics, we can easily report on any kind of object data.
by Terry Voss
Feedback
Average Rating: 
Views (Total / Last 10 Days): 31948/ 23

 

Introduction 

Microsoft has gone to great lengths to make it easy, requiring less code, with more drag and drop, to bind DataSets to DataGridViews and ReportViewer controls. What if you use strongly typed collections versus DataSets though? And what if you want one ReportViewer Control to preview and print all your application’s reports instead of using one ReportViewer control for each report of the 200 your accounting system will use?
ReportViewer Source    

How to Instantiate a Collection Full of Data

Visual Studio knows how DataSets are populated, but may not know as much about your favorite ORMapper Collection type. If you create a new Data Source, but choose Data Source Type Object instead of Database, at runtime, the ReportViewer Control will know how to create a default constructor instance of that object, which might be a collection, especially since in reports we often tend to report many rows of data. Your collection may have a complex method for instantiation of a filtered and sorted collection of some type of object. If you subclass that collection with a collection whose default constructor knows how to fill the contents you want and then bind to that instance, the ReportViewer displays it fine, but you may find yourself subclassing a lot of types that way.

Another way is to tell the ReportViewer the proper type of collection, so that the proper set of data fields can be dragged onto the report form. Then, just before runtime, switch that empty instance of the collection out with a properly filtered and sorted collection with code like this.

    Me.BindingSource1.DataSource = myDatasource
    Me.ReportViewer1.RefreshReport()


This assumes your ReportViewer1 Control has a System.Windows.Forms.BindingSource Control named BindingSource1 associated with it, where myDatasource was instantiated like this in LLBLGen Pro, the popular ORMapper.

Dim myDatasource as New CustomerCollection

Dim typeFilter as ipredicate = CustomerFields.Type = 1

Dim nameSort as isortexpression = New SortExpression

nameSort.add(sortclausefactory.create(CustomerFieldIndex.Name,Ascending))

Your collection’s instantiation would differ.

When I first tried this, no fields of interest showed up in my new Data Source for dragging onto the Report surface. This is when I learned the value of using Generic collections with code like in Code Listing #1.


Note: This collection had every property that my report required in one file, so no relation needed to be supported; parent properties are at the top for the header, and child properties,which will go into a table of rows in the report, are at the bottom.

 

Code Listing #1

Imports system.collections.generic
 
Public Class PoItems : Inherits List(Of PoItem) ' from the above line the Data Source gets drag/drop with the fields below
End Class
Public Class PoItem
  ' Parent properties
  Dim _poID As Integer
  Dim _vendor As String
  Dim _vAddr1 As String
  Dim _vAddr2 As String
  Dim _vcsz As String
  Dim _shipVia As String
  Dim _shipName As String
  Dim _shipAddr1 As String
  Dim _shipAddr2 As String
  Dim _scsz As String
  Dim _orderDate As DateTime
  Dim _received As Boolean
 
  ' Child properties
  Dim _prodNo As String
  <span lang=PT-BR>Dim _description As String</span>
<span lang=PT-BR>  Dim _quantity As Integer</span>
<span lang=PT-BR>  Dim _rate As Decimal</span>
<span lang=PT-BR>  Dim _retail As Decimal</span>
<span lang=PT-BR> </span>
<span lang=PT-BR>  </span>Public Property POID() As Integer
    Get
      Return _poID
    End Get
    Set(ByVal value As Integer)
      _poID = value
    End Set
  End Property
 
  ‘ a few lines of code are removed here though they are important for binding
  .
  .
  .
 
  Public Property Retail() As Decimal
    Get
      Return _retail
    End Get
    Set(ByVal value As Decimal)
      _retail = value
    End Set
  End Property
 
End Class

How Can We Get One Form With a ReportViewer Control to Display 200 Reports?

There are four things that must be coordinated to allow a report to occur:
1) A ReportViewer control
2) A Report or .rdlc file, which is xml
3) A Datasource like a Dataset or a Collection
4) A BindingSource

Because there are four things to bind together, we need a special kind of glue. It is called the ReportDataSource Class. See how it connects everything together below.

Note the string: myProj_EsnItems is constructed with exact case like this:

ProjectName_ClassName.


Similarly, "myProj.Report1.rdlc" is constructed from exact case with ProjectName.ReportName.

If these strings do not use exact case spelling, the reports will not run.

 

Code Listing#2

 
Public Class Form2
 
  Private Sub Form2_Load(ByVal sender As System.ObjectByVal e As 
    System.EventArgs) Handles MyBase.Load
    If Utility.WhichReport = 1 Then
      Dim rptSource1 As New Microsoft.Reporting.WinForms.ReportDataSource
      rptSource1.name = "myProj_EsnItems"
      rptSource1.value = Me.BindingSource1
      Me.ReportViewer1.LocalReport.DataSources.Add(rptSource1)
 Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "myProj.Report1.rdlc"
      Dim eitems As New EsnItems
      Dim eitem As New EsnItem
      Dim eitem1 As New EsnItem
      eitem.SerialNo = "778889"
      eitem.ModelNo = "244444444"
      eitem.OrderNo = "233333332"
      eitems.Add(eitem)
      eitem1.SerialNo = "99333333"
      eitem1.ModelNo = "44444455555"
      eitem1.OrderNo = "3222222223"
      eitems.Add(eitem1)
      Me.BindingSource1.DataSource = eitems
    Else
      Dim rptSource2 As New Microsoft.Reporting.WinForms.ReportDataSource
      rptSource2.name = "myProj_PoItems"
      rptSource2.value =  Me.BindingSource1
     Me.ReportViewer1.LocalReport.DataSources.Add(rptSource2)
      Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "myproj.Report2.rdlc"
      Dim items As New PoItems
      Dim item As New PoItem
      Dim item1 As New PoItem
      item.ProdNo = "2222222"
      item.Quantity = "2"
      item.Retail = "66"
      items.Add(item)
      item1.ProdNo = "1111111"
      item1.Quantity = "1"
      item1.Retail = "55"
      items.Add(item1)
      Me.BindingSource1.DataSource = items
    End If
    Me.ReportViewer1.RefreshReport()
  End Sub
 
End Class

You can get the source code from the above code listing from this article's download.

Conclusion

Microsoft has told me that this type of code only works for Windows and not with the Web version of these classes. So for web versions, create multiple ReportViewer controls on one form with only one visible at a time for multiple form reporting; otherwise just use one form per report.

Now we have a local reporting system that previews, prints, etc., supported solely by Visual Studio that we can have a lot of control over by manipulating the its properties, and any kind of Object can be our data.

 



User Comments

Title: Trouble generating multiple reports   
Name: Larry
Date: 2012-04-18 6:24:57 PM
Comment:
I tried to use the following code to generate a different report. This only works the first time a user selects one of 2 reports from a combo box. So if I select the variance report, the report is generated. But if I then select the other report, it is not generated and I only see the first report.

Private Sub ComboBoxReports_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBoxReports.SelectedIndexChanged

Select Case ComboBoxReports.Text
Case "Variance"
'rptSource.Value = Me.InventoryHistoryBindingSource
'rptSource.Name = "Inventory Variance Report"
Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "InventoryModule.VarianceReport.rdlc"
Me.InventoryHistoryTableAdapter.Fill(Me.InventoryDataSet36.InventoryHistory)
Case "Current Inventory"
Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "InventoryModule.CurrentInventoryReport.rdlc"
Me.InventoryTableAdapter.Fill(Me.InventoryDataSet23.Inventory)
End Select
Me.ReportViewer1.RefreshReport()
Title: Very Nice   
Name: Yonathan
Date: 2011-06-13 9:23:37 AM
Comment:
It is a very important lecture and of experience sharing. I liked it the way it works. Most of the time, people get confused working with ReportViewer, including me.
I have one question, anybody may help me here. What is the equivalent of the BindSource(Datasource Object) for asp.net application? There are different source objects.
Title: Thanks   
Name: see
Date: 2010-02-21 8:17:40 PM
Comment:
helpful!!
Title: Hai   
Name: Name
Date: 2009-09-21 11:08:24 AM
Comment:
hai i need Report.. without drag and drop the column name only run time based on dataset auto column generate is it possible
Title: Its very Good,But   
Name: Rajesh
Date: 2009-06-12 8:04:48 AM
Comment:
This is working Good,
But in my Application i have to send parameter value through coding means i am not setting any parameter value at script and i have to arrange Fields through coding.
Can Anyone help me

Thanking You Advance
My Email Is rajeshpv.ece@gmail.com
Title: can be done with web reports also   
Name: Ranjeeth
Date: 2009-01-08 3:21:08 PM
Comment:
The above can be done with web reports also
with the following steps

string sqlQuery = "select regn as Region, count(*) as Totlost, count(*) as TotPaid, sum(claim_amt) as TotDolPaid from DummyClaim" +
" where devision = 'AP' group by regn order by regn";
// the above sql query and below runsql function returns dataset which is stored in dataSet.
dataSet = DAL.RunSQLQuery(sqlQuery, "Region");

// i am using one of the tables of dataset as resource datasource
ReportDataSource rds1 = new ReportDataSource("Reports_Region", dataSet.Tables[1]);
//1st step is to reset
ReportViewer1.Reset();
// 2nd step is to set report path
ReportViewer1.LocalReport.ReportPath = Server.MapPath("Level1.rdlc");
// 3rd clear datasources
ReportViewer1.LocalReport.DataSources.Clear();
// 4th add data source
ReportViewer1.LocalReport.DataSources.Add(rds1);
//5th databind
ReportViewer1.DataBind();

// 6th refresh
ReportViewer1.LocalReport.Refresh();


repeat the steps for every if else condition so that you can use the same reportviewer for all reports

imp: dont use gui smart tag for setting the initial report in the report viewer leave it blank
Title: It works, but   
Name: Gurpreet Singh
Date: 2008-06-17 7:25:20 PM
Comment:
Hi,
I was looking for this solution from so many days, thanks to the author for this
But following this has made my application respond slowly.

I want to bind 8 reports to one report viewer control and that is increasing the delay time.

I think as we are embedding the report to the report viewer control, we should also release the report from the control.

can i get code to do that? contact me on
gurpreetintown at gmail dot com
Title: Change header per customer   
Name: Terry Voss
Date: 2008-04-25 12:06:31 PM
Comment:
If you report on a customer table with a one to one relation to a table with your header in it that is a simple way to report different header for each customer. If the header data is in customer file as a field it is even easier.

Using the ssrs web service you can report 400 bills for 400 customers as 400 reports. This allows footer to say 1 of 5 pages, 2 of 5 pages, etc. recently had to do this. ssrs can do about anything.
Title: How to display a record per page?   
Name: Jon
Date: 2008-04-25 7:26:45 AM
Comment:
Does anyone know if its possible to use this control for displaying multiple invoices as seperate pages within the same report....i.e. The header changes on change of customer and the invoice lines are displayed??

I really can't figure it out and any help would be greatly appreciated!
Thanks in advance
Title: how to display different reports in same viewer other way   
Name: egarcia
Date: 2006-07-08 8:20:37 PM
Comment:
To this use the same XML schema into multiples report.

Sub CHangeReport

' clear reportviewer
Me.ReportViewer1.Reset()

' Reset DataBindings
MyResetReportViewer()

' change the report source
Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "idealservices.Report2.rdlc"

'Again refresh Control
Me.ReportViewer1.LocalReport.Refresh()

End SUB

Listo !!!
Title: how to display different reports in same viewer other way   
Name: egarcia
Date: 2006-07-08 8:18:22 PM
Comment:
To reset a reportviewer copy the constructor lines located in the file FORMNAME.designer.vb made by VS2005 designer, these lines contains the code used in the reportviewer designer. Use these lines to make a procedure to change before the ReportEmbeddedResource property.

example:


Private Sub MyResetReportViewer()
Dim ReportDataSource1 As Microsoft.Reporting.WinForms.ReportDataSource = New Microsoft.Reporting.WinForms.ReportDataSource
Dim ReportDataSource2 As Microsoft.Reporting.WinForms.ReportDataSource = New Microsoft.Reporting.WinForms.ReportDataSource
Dim ReportDataSource3 As Microsoft.Reporting.WinForms.ReportDataSource = New Microsoft.Reporting.WinForms.ReportDataSource
Dim ReportDataSource4 As Microsoft.Reporting.WinForms.ReportDataSource = New Microsoft.Reporting.WinForms.ReportDataSource
Dim ReportDataSource5 As Microsoft.Reporting.WinForms.ReportDataSource = New Microsoft.Reporting.WinForms.ReportDataSource
ReportDataSource1.Name = "siemDataSet1_Cuadrilla"
ReportDataSource1.Value = Me.RPTCuadrillaBindingSource1
ReportDataSource2.Name = "siemDataSet1_Inventario"
ReportDataSource2.Value = Me.RPTInventBindingSource1
ReportDataSource3.Name = "siemDataSet_Grupos"
ReportDataSource3.Value = Me.GruposBindingSource
ReportDataSource4.Name = "siemDataSet1_Resumen"
ReportDataSource4.Value = Me.ResumenBindingSource
ReportDataSource5.Name = "siemDataSet_Inventario"
ReportDataSource5.Value = Me.RPTInventBindingSource1
Me.ReportViewer1.LocalReport.DataSources.Add(ReportDataSource1)
Me.ReportViewer1.LocalReport.DataSources.Add(ReportDataSource2)
Me.ReportViewer1.LocalReport.DataSources.Add(ReportDataSource3)
Me.ReportViewer1.LocalReport.DataSources.Add(ReportDataSource4)
Me.ReportViewer1.LocalReport.DataSources.Add(ReportDataSource5)
End Sub

To this use the same XML schema into mul
Title: vb.net   
Name: rajeev prasad
Date: 2006-04-16 8:55:46 AM
Comment:
plz send me the vb.net of all text guide

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-19 10:58:47 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search