1
votes

I have an MDI application with many different Forms. Then I have a ReportViewer control inside a ReportForm where I dynamically load RDLC reports.

The ReportForm is opened from the other Forms with a button click. Inside the ReportForm, above the ReportViewer control, there's a ComboBox with a list of available reports for that specific Form. That list is dynamically generated based on the Form from which the ReportViewer was opened. The list is generated by looking for a Form associated folder, inside the Reports Project (which output is a DLL), that contains the corresponding .rdlc files.

The question is this:

Since the RDLC are all already generated and configured with Report Designer and binded to specific DataSet and DataTable, why do I need to recreate everytime the ReportDataSource object (DataSet, DataTable) and rebind it to the report?

My actual code:

// Code used to open a new ReportForm Window

private void bindingNavigatorViewReport_Click(object sender, EventArgs e)
{
    // If the form is already open, then Focus on it
    if (MdiMain.IsFormOpen(ApplicationForm.ReportForm))
    {
        Application.OpenForms[formName].Focus();
    }
    // I need to instantiate a new ReportForm
    else
    {
        /* The parameters are:
        * - ApplicationForm enum which identifies the Form;
        * - The Form corresponding DataSet
        */
        ReportForm reportForm = new ReportForm(ApplicationForm.SchoolForm, SchoolDataSet)
        {
            MdiParent = MdiParent,
            Icon = Resources.ViewReportIcon
        };
        reportForm.Show();
    }
}


// Code used to load report

/* In the ReportForm class I have a method that casts the parameters to the
 * specific types. So I can obtain the DataSet to use.
 */

// Report Path inside DLL assembly taken from SelectedValue of ComboBox
string reportSource = cmbReport.SelectedValue.ToString();
// Get report Stream from DLL Path
Stream reportStream = assembly.GetManifestResourceStream(reportSource);
// Load report Stream
reportViewer1.LocalReport.LoadReportDefinition(reportStream);

/* Create a new DataSource with DataSet name and DataTable object
 * The DataTable object must come from DataSet, ie: DataSet.SchoolListDataTable.
 */
ReportDataSource rds = new ReportDataSource("DataSetName", DataTableObject);

reportViewer1.LocalReport.DataSources.Clear();
reportViewer1.LocalReport.DataSources.Add(rds);
reportViewer1.LocalReport.Refresh();

The main problem is that I have no "dynamic way" to get from the Forms the DataTable object for every report associated to the Form, since when I click on the "Open report window" button, I have a list of reports and so on DataSet (associated to that Form) but many different DataTables, not just one.

** UPDATE: Final solution **

Finally I found that when I load the report stream I can use this code:

// Loads all the data sources associated to the current selected report
IEnumerable<string> dsNames = reportViewer1.LocalReport.GetDataSourceNames();
foreach (string dsName in dsNames)
{
    // dsName is equal to the string "V_REP_SCHOOLS" that is the corresponding DataTable name
    ReportDataSource rds = new ReportDataSource(dsName, _currentReportDataSet.Tables[dsName]);
    reportViewer1.LocalReport.DataSources.Add(rds);
}

After loading the report stream (look at the code above), the GetDataSourceNames() method gives me all the DataSource (in effect DataSet if you look on the Report Data panel in the report designer) that is named inside the report. So I named my DataSet as the DataTable from which I need to load my data, such as "V_REPORT_SCHOOLS", and then I loaded the DataTable by name from my DataSet.

2
Which object do you want to make dynamic? Give an example. - Reza Aghaei
For each different Form of the MDI I've one or more .rdlc reports. The DataSet is the same for each report of a specific Form, but the DataTables are different. So when I call the ReportForm I can pass the DataSet class (XSD schema) as a parameter but not the specific DataTable, because there're many. I need that. Please take a look again at the question for the code update. - Cheshire Cat

2 Answers

1
votes

Here is the main idea: You can have a mapping between your reports and your data table names and simply access data tables by name.

You can simply access to a data table in your DataSet by its name using Tables property:

datasetInstance.Tables["SchoolListDataTable"]

So it's enough to have a mapping between your reports and your data tables somewhere.
You can use settings or any thing else for this mapping. for example this mapping can be a simple key-value Dictionary<string,string> having your report name or whatever you have in your combobox as Key and your data table name as Value:

var mapping = new Dictionary<string, string>();
mapping.Add("report1", "table1");
mapping.Add("report2", "table2");

and then use selected value of combobox to retrieve required table name from that mapping dictionary:

//Find selected report
var reportSource = cmbReport.SelectedValue.ToString();

//Load report definition
Stream reportStream = assembly.GetManifestResourceStream(reportSource);
reportViewer1.LocalReport.LoadReportDefinition(reportStream);

//I supppose you use whatever you put in combobox, as mapping key
//Find the table name 
var tableName = mapping[reportSource];

//Set report data source
ReportDataSource rds = new ReportDataSource("DataSetName", datasetInstance.Tables[tableName]);
0
votes

Report doesn't store DataSet information

When you're creating your report, you select your DataSource so that the designer may collect metadata from it (Column names, data types, etc). It then pretty much forgets it except for the fact it creates a dataset file (.xsd).

From there when you're loading the report you need to initialize a ReportDataSource telling it where to get the data from, because the report itself (.rdlc file) does not store this information.

Think of it as a sort of separation of concerns to prevent coupling. I believe (not certain) it's so that you can have 2 table schemas that are identical in two different locations (Let's say Development environment and Production environment) and point it at either of them. Additionally I believe you can set reports to load from things other than databases, such as xml files if you desired which leads partially to another reason it is this way.