为什么加载 RDLC 报表时需要重新创建 DataSource?
Why do I need to recreate DataSource when I load a RDLC report?
我有一个 MDI 应用程序,其中包含许多不同的表单。然后我在 ReportForm
中有一个 ReportViewer
控件,我在其中动态加载 RDLC 报告。
ReportForm
是通过单击按钮从其他表单打开的。
在 ReportForm
内,在 ReportViewer
控件上方,有一个 ComboBox
,其中包含该特定表单的可用报告列表。该列表是根据打开 ReportViewer 的表单动态生成的。
该列表是通过在 Reports 项目(其输出是一个 DLL)中查找一个 Form 关联文件夹生成的,其中包含相应的 .rdlc 文件。
问题是这样的:
既然 RDLC 都已经用报表设计器生成和配置并绑定到特定的数据集和数据表,为什么我每次都需要重新创建 ReportDataSource 对象(数据集、数据表)并将其重新绑定到报告?
我的实际代码:
// 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();
主要问题是我没有 "dynamic way" 从 Forms 获取与 Form 关联的每个报表的 DataTable 对象,因为当我单击 "Open report window" 按钮时,我有一个报告列表等数据集(与该表格相关联)但许多不同的数据表,而不仅仅是一个。
** 更新:最终解决方案 **
最后我发现当我加载报告流时我可以使用这个代码:
// 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);
}
加载报表流后(查看上面的代码),GetDataSourceNames()
方法为我提供了所有数据源(实际上 DataSet
如果您查看报表设计器中的报表数据面板) 在报告中命名。
所以我将我的 DataSet 命名为我需要从中加载数据的 DataTable,例如 "V_REPORT_SCHOOLS",然后我从我的 DataSet 中按名称加载 DataTable。
报表不存储数据集信息
当您创建报表时,您 select 您的数据源,以便设计人员可以从中收集元数据(列名、数据类型等)。然后它几乎忘记了它,除了它创建了一个数据集文件 (.xsd).
从那里加载报告时,您需要初始化一个 ReportDataSource
告诉它从哪里获取数据,因为报告本身(.rdlc 文件)不存储此信息。
将其视为一种防止耦合的关注点分离。我相信(不确定)这样你就可以在两个不同的位置(比如开发环境和生产环境)拥有 2 个相同的 table 模式并将其指向其中任何一个。此外,我相信您可以将报告设置为从数据库以外的其他内容加载,例如 xml 文件,如果您愿意,这部分导致了这种方式的另一个原因。
这里是主要思想:您可以在报告和数据 table 名称之间建立映射,然后只需按名称访问数据 table。
您可以使用 Tables
属性:
通过名称简单地访问 DataSet
中的数据 table
datasetInstance.Tables["SchoolListDataTable"]
因此,在您的报告和数据之间建立一个映射 table 某处就足够了。
您可以为此映射使用设置或任何其他内容。例如,此映射可以是一个简单的键值 Dictionary<string,string>
具有您的报告名称或 您的组合框中的任何内容 作为 Key
和您的数据 table 命名为 Value
:
var mapping = new Dictionary<string, string>();
mapping.Add("report1", "table1");
mapping.Add("report2", "table2");
然后使用组合框的选定值从该映射字典中检索所需的 table 名称:
//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]);
我有一个 MDI 应用程序,其中包含许多不同的表单。然后我在 ReportForm
中有一个 ReportViewer
控件,我在其中动态加载 RDLC 报告。
ReportForm
是通过单击按钮从其他表单打开的。
在 ReportForm
内,在 ReportViewer
控件上方,有一个 ComboBox
,其中包含该特定表单的可用报告列表。该列表是根据打开 ReportViewer 的表单动态生成的。
该列表是通过在 Reports 项目(其输出是一个 DLL)中查找一个 Form 关联文件夹生成的,其中包含相应的 .rdlc 文件。
问题是这样的:
既然 RDLC 都已经用报表设计器生成和配置并绑定到特定的数据集和数据表,为什么我每次都需要重新创建 ReportDataSource 对象(数据集、数据表)并将其重新绑定到报告?
我的实际代码:
// 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();
主要问题是我没有 "dynamic way" 从 Forms 获取与 Form 关联的每个报表的 DataTable 对象,因为当我单击 "Open report window" 按钮时,我有一个报告列表等数据集(与该表格相关联)但许多不同的数据表,而不仅仅是一个。
** 更新:最终解决方案 **
最后我发现当我加载报告流时我可以使用这个代码:
// 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);
}
加载报表流后(查看上面的代码),GetDataSourceNames()
方法为我提供了所有数据源(实际上 DataSet
如果您查看报表设计器中的报表数据面板) 在报告中命名。
所以我将我的 DataSet 命名为我需要从中加载数据的 DataTable,例如 "V_REPORT_SCHOOLS",然后我从我的 DataSet 中按名称加载 DataTable。
报表不存储数据集信息
当您创建报表时,您 select 您的数据源,以便设计人员可以从中收集元数据(列名、数据类型等)。然后它几乎忘记了它,除了它创建了一个数据集文件 (.xsd).
从那里加载报告时,您需要初始化一个 ReportDataSource
告诉它从哪里获取数据,因为报告本身(.rdlc 文件)不存储此信息。
将其视为一种防止耦合的关注点分离。我相信(不确定)这样你就可以在两个不同的位置(比如开发环境和生产环境)拥有 2 个相同的 table 模式并将其指向其中任何一个。此外,我相信您可以将报告设置为从数据库以外的其他内容加载,例如 xml 文件,如果您愿意,这部分导致了这种方式的另一个原因。
这里是主要思想:您可以在报告和数据 table 名称之间建立映射,然后只需按名称访问数据 table。
您可以使用 Tables
属性:
DataSet
中的数据 table
datasetInstance.Tables["SchoolListDataTable"]
因此,在您的报告和数据之间建立一个映射 table 某处就足够了。
您可以为此映射使用设置或任何其他内容。例如,此映射可以是一个简单的键值 Dictionary<string,string>
具有您的报告名称或 您的组合框中的任何内容 作为 Key
和您的数据 table 命名为 Value
:
var mapping = new Dictionary<string, string>();
mapping.Add("report1", "table1");
mapping.Add("report2", "table2");
然后使用组合框的选定值从该映射字典中检索所需的 table 名称:
//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]);