【问题标题】:Why do I need to recreate DataSource when I load a RDLC report?为什么我在加载 RDLC 报告时需要重新创建 DataSource?
【发布时间】:2015-10-16 10:48:02
【问题描述】:

我有一个包含许多不同表单的 MDI 应用程序。然后我在 ReportForm 中有一个 ReportViewer 控件,我在其中动态加载 RDLC 报告。

ReportForm 通过单击按钮从其他表单打开。 在ReportForm 内部,ReportViewer 控件上方有一个ComboBox,其中包含该特定表单的可用报告列表。该列表是根据打开 ReportViewer 的表单动态生成的。 该列表是通过在 Reports 项目(其输出是一个 DLL)中查找与表单相关的文件夹生成的,该文件夹包含相应的 .rdlc 文件。

问题是这样的:

既然 RDLC 都已经通过报表设计器生成和配置并绑定到特定的 DataSet 和 DataTable,为什么我每次都需要重新创建 ReportDataSource 对象(DataSet、DataTable)并将其重新绑定到报表?强>

我的实际代码:

// 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();

主要问题是我没有“动态方式”从表单中获取与表单关联的每个报表的 DataTable 对象,因为当我单击“打开报表窗口”按钮时,我有一个报表列表等等 DataSet(与该表单相关联),但有许多不同的 DataTables,而不仅仅是一个。

** 更新:最终解决方案 **

最后我发现,当我加载报告流时,我可以使用这段代码:

// 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 命名为我需要从中加载数据的 DataTable,例如“V_REPORT_SCHOOLS”,然后我从我的 DataSet 中按名称加载了 DataTable。

【问题讨论】:

  • 你想让哪个对象动态化?举个例子。
  • 对于每种不同形式的 MDI,我都有一个或多个 .rdlc 报告。 DataSet对于特定表单的每个报告都是相同的,但DataTables是不同的。因此,当我调用 ReportForm 时,我可以将 DataSet 类(XSD 模式)作为参数传递,但不能传递特定的 DataTable,因为有很多。我需要它。请再次查看代码更新问题。

标签: c# winforms reporting-services datatable datasource


【解决方案1】:

这里的主要思想是:您可以在报告和数据表名称之间建立映射,并且只需按名称访问数据表。

您可以使用Tables 属性简单地通过名称访问DataSet 中的数据表:

datasetInstance.Tables["SchoolListDataTable"]

因此,在您的报告和数据表之间建立一个映射就足够了。
您可以为此映射使用设置或其他任何东西。例如,此映射可以是一个简单的键值对 Dictionary&lt;string,string&gt;,您的报告名称或组合框中的任何内容为Key,您的数据表名称为Value:

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

然后使用组合框的选定值从该映射字典中检索所需的表名:

//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]);

【讨论】:

  • 这是我正在考虑和测试的解决方案。但我宁愿不必手动初始化 Dictionary 或任何结构。所以最后我发现,当我加载报告流时,我可以使用IEnumerable&lt;string&gt; dsNames = reportViewer1.LocalReport.GetDataSourceNames();,它为我提供了在报告中命名的所有数据源(实际上DataSet 在报告设计器中查看)。所以我将我的数据集命名为数据表名称,例如“V_REPORT_SCHOOLS”,然后我从数据集中按名称加载数据表,如下所示:DataSetName.Tables["dsName"]。查看我的更新!
  • @CheshireCat 感谢您的反馈 :) 使用 DataSet 名称而不是 Disctionary 是一个非常好的选项 +1,其余的解决方案是相同的,基于查找具有名称的表。
【解决方案2】:

报表不存储数据集信息

当您创建报表时,您可以选择您的数据源,以便设计人员可以从中收集元数据(列名、数据类型等)。然后它几乎忘记了它,只是它创建了一个数据集文件 (.xsd)。

当您加载报告时,您需要从那里初始化一个ReportDataSource,告诉它从哪里获取数据,因为报告本身(.rdlc 文件)不存储此信息。

将其视为一种关注点分离,以防止耦合。我相信(不确定)这样您就可以在两个不同的位置(比如说开发环境和生产环境)拥有两个相同的表模式并将其指向其中任何一个。此外,我相信您可以将报告设置为从数据库以外的其他内容(例如 xml 文件)加载,如果您愿意,这部分导致了另一个原因。

【讨论】:

  • 好的,我知道了。所以现在我知道它为什么会这样工作了。那么,我该如何管理我的DataSet 和不同的DataTables?我尝试使用反射,通过使用以下方式获取 DataTable:string dataTableNamespace = _currentReportDataSetType + "." + dataSetName + "DataTable"; Type t = Type.GetType(dataTableNamespace); DataTable dt = (DataTable)Activator.CreateInstance(t);,但它不起作用......dataTableNamespace 的结果字符串是正确的,但返回的Type 始终是null。
  • 当我使用它时,我只是将每个报告的连接信息存储在数据库中。我不确定确切的过程,但我相信我只是做了一个普通的 SqlConnection 并将我的数据检索为一个 DataSet 对象,从那里将它提供给报告(使用该 DataSet 对象来构造 ReportDataSource)。但是时间太长了,具体细节我记不得了。此外,我对 ReportViewer 进行了一种奇怪的 MVC 用法,因此这可能不是其他架构中的最佳方式。
猜你喜欢
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多